主键与外键、自增、表关系之一对多、多对多、一对一

约束条件之主键

1.主键介绍
	我们在建立数据库的时候,需要为每张表指定一个主键,所谓主键就是能够唯一标识表中某一行的属性或属性组,一个表只能有一个主键,因为主键可以唯一标识某一行记录,所以可以确保执行数据更新、删除的时候不会出现张冠李戴的错误。主键除了上述作用外,常常与外键构成参照完整性约束,防止出现数据不一致。所以数据库在设计时,主键起到了很重要的作用。
2.主键的关键字:primary key
3.主键特点
	i.单从约束角度上而言主键等价于非空且唯一  not null unique,验证:
        mysql> create database db;
        Query OK, 1 row affected (0.00 sec)

        mysql> use db;
        Database changed
        mysql> create table t1(
            ->            id int primary key,
            ->            name varchar(32)
            ->         );
        Query OK, 0 rows affected (0.03 sec)

        mysql> select * from t1;
        Empty set (0.00 sec)

        mysql> desc t1;
        +-------+-------------+------+-----+---------+-------+
        | Field | Type        | Null | Key | Default | Extra |
        +-------+-------------+------+-----+---------+-------+
        | id    | int(11)     | NO   | PRI | NULL    |       |
        | name  | varchar(32) | YES  |     | NULL    |       |
        +-------+-------------+------+-----+---------+-------+
        2 rows in set (0.03 sec)

        mysql> insert into t1(name) values('tony');
        # 'ERROR 1364 (HY000): Field 'id' doesn't have a default value'
        mysql> insert into t1(id) values(1);
        Query OK, 1 row affected (0.02 sec)

        mysql> insert into t1(id) values(1);
        ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
            
	ii.InnoDB存储引擎规定一张表必须有且只有一个主键
    	·如果创建的表中没有主键也没有非空且唯一的字段,那么InnoDB存储引擎会自动采用一个隐藏的字段作为主键(主键可以加快数据查询:新华字典的目录)
        ·如果创建的表中没有主键但是有非空且唯一的字段,那么InnoDB存储引擎会自动将该字段设施为主键
        	mysql> create table t2(
                -> nid int not null unique,
                -> sid int not null unique,
                -> uid int not null unique,
                -> name varchar(32));
            Query OK, 0 rows affected (0.05 sec)

            mysql> desc t2;
            +-------+-------------+------+-----+---------+-------+
            | Field | Type        | Null | Key | Default | Extra |
            +-------+-------------+------+-----+---------+-------+
            | nid   | int(11)     | NO   | PRI | NULL    |       |
            | sid   | int(11)     | NO   | UNI | NULL    |       |
            | uid   | int(11)     | NO   | UNI | NULL    |       |
            | name  | varchar(32) | YES  |     | NULL    |       |
            +-------+-------------+------+-----+---------+-------+
            4 rows in set (0.03 sec)
	iii.创建表的时候都应该有一个'id'字段,并且该字段应该作为主键
    	比如说:uid、sid、pid
补充说明:争对主键有且只有一个
	单列主键
		id int primary key
	联合主键:
		primary key(sid, nid)
		实操:
		mysql> alter table t2 add primary key(nid,sid);
		Query OK, 0 rows affected (0.06 sec)
		Records: 0  Duplicates: 0  Warnings: 0

		mysql> desc t2;
		+-------+-------------+------+-----+---------+-------+
		| Field | Type        | Null | Key | Default | Extra |
		+-------+-------------+------+-----+---------+-------+
		| nid   | int(11)     | NO   | PRI | NULL    |       |
		| sid   | int(11)     | NO   | PRI | NULL    |       |
		| uid   | int(11)     | NO   | UNI | NULL    |       |
		| name  | varchar(32) | YES  |     | NULL    |       |
		+-------+-------------+------+-----+---------+-------+
		4 rows in set (0.03 sec)

没有办法更新主键,主键需先删除才能增加
alter table [表名] drop primary key;

约束条件之主键的自增

当对应的字段不给值,或者说给默认值或者给null的时候,会自动的被系统触发,系统会从当前字段中已有的最大值再进行+1操作,得到一个新的再不同的字段

自增长字段必须是数字,而且是整型


mysql> select * from t4;
+----+------+
| id | name |
+----+------+
|  1 | aa   |
|  2 | bb   |
|  3 | cc   |
+----+------+
3 rows in set (0.01 sec)

mysql> insert into t4(id,name) values(4,'aa'),(5,'bb'),(6,'cc');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t4;
+----+------+
| id | name |
+----+------+
|  1 | aa   |
|  2 | bb   |
|  3 | cc   |
|  4 | aa   |
|  5 | bb   |
|  6 | cc   |
+----+------+
6 rows in set (0.00 sec)

mysql> insert into t4(name) values('aa'),('bb'),('cc');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t4;
+----+------+
| id | name |
+----+------+
|  1 | aa   |
|  2 | bb   |
|  3 | cc   |
|  4 | aa   |
|  5 | bb   |
|  6 | cc   |
|  7 | aa   |
|  8 | bb   |
|  9 | cc   |
+----+------+
9 rows in set (0.00 sec)
自增长如果对应的字段输入了值,那么自增长失效;但是下一次还是能够正确的自增长(从最大值+1)
1.auto_increment
	该约束条件不能单独使用,必须跟在键后面(配合主键主键使用)
    mysql> create table t3(
        -> id int auto_increment);
    ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
	'''这里只能有一个自动增加的列,并且必须是一个键'''
    
    正确的写法:
    mysql> create table t4(
        -> id int primary key auto_increment,
        -> name varchar(32));
    Query OK, 0 rows affected (0.05 sec)
    
    mysql> desc t4;
    +-------+-------------+------+-----+---------+----------------+
    | Field | Type        | Null | Key | Default | Extra          |
    +-------+-------------+------+-----+---------+----------------+
    | id    | int(11)     | NO   | PRI | NULL    | auto_increment |
    | name  | varchar(32) | YES  |     | NULL    |                |
    +-------+-------------+------+-----+---------+----------------+
    2 rows in set (0.01 sec)
    mysql> insert into t4(name) values('zz'),('aa'),('bb');
    Query OK, 3 rows affected (0.02 sec)
    Records: 3  Duplicates: 0  Warnings: 0
	'''主键字段不需要添加自动生成,只需要去添加数据就好'''
    mysql> select * from t4;
    +----+------+
    | id | name |
    +----+------+
    |  1 | zz   |
    |  2 | aa   |
    |  3 | bb   |
    +----+------+
    3 rows in set (0.00 sec)
    
    这里需要注意的主键只会向后增加,不管中途是怎么操作了数据,它只会向后增加,例如:如果删除了id=3的数据,接下来在向里面添加数据它也是从4开始,那么这样也好,如果中途删了数据,我们就会从id号上查看到,总而言之就是自增不会因为删除的操作而进行回退,只会向后增加
    mysql> delete from t4 where id=3;
    Query OK, 1 row affected (0.00 sec)

    mysql> insert into t4(name) values('zz'),('aa'),('bb');
    Query OK, 3 rows affected (0.02 sec)
    Records: 3  Duplicates: 0  Warnings: 0

    mysql> select * from t4;
    +----+------+
    | id | name |
    +----+------+
    |  1 | zz   |
    |  2 | aa   |
    |  4 | zz   |
    |  5 | aa   |
    |  6 | bb   |
    +----+------+
    5 rows in set (0.00 sec)
    针对自增以上的这个特性,如果我们非要重置,那么可以使用格式化表
    	truncate 表名; 它的作用就是删除表数据并重置主键值
        

约束条件之外键

image-20220816165327824

1.外键前戏
	需要创建一张员工表
		id	name gender	dep_name dep_desc
	上述表的缺陷
    	1.表结构不清晰 到底是员工表还是部门表(不严重 无所谓)
   		2.字段数据反复存取 浪费存储空间(不严重 无所谓)
        3.表的扩展性极差 牵一发动全身(很严重 效率极低)
	优化操作>>>:拆表
		id	name	  gender
		id	dep_name  dep_desc
       拆表之后解决了上述的三个问题,但是出现了一个致命的缺陷,不知道员工对应的部门,部门里也不知道存在哪些员工
   	解决措施:在员工表里增加'dep_id'
    	id		name	gender 	  dep_id
       添加一个部门编号字段填写部门数据的主键值
   	外键字段
    	专门用于记录表与表之间数据的关系
2.外键字段的创建
	外键字段是用来记录表与表之间数据的关系 而数据的关系有四种
		一对多关系、多对多关系、一对一关系、没有关系
	表数据关系的判定  >>>: '换位思考'
      针对员工表和部门表判断数据关系
		1.先站在员工表的角度
			问:一条员工数据能否对应多条部门数据
			翻:一名员工能否属于多个部门
			答:不可以
		2.再站在部门表的角度
			问:一条部门数据能否对应多条员工数据
			翻:一个部门能否拥有多个员工
			答:可以
		完成换位思考之后得出的答案 一个可以一个不可以
		那么表关系就是"一对多"
			部门是一 员工是多
		"""针对'一对多'的关系 外键字段建在多的一方 """
		ps:没有多对一 统一称为'一对多'

表关系之一对多

"""涉及到外键字段,先写普通字段,然后再写外键字段"""
create table emp(
	id int primary key auto_increment,
    name varchar(32),
    gender enum('male','female') default 'male',
    dep_id int,
    foreign key(dep_id) references dep(id)
);

create table dep(
	id int primary key auto_increment,
    dep_name varchar(32),
    dep_desc varchar(32)
);
mysql> insert into dep(dep_name,dep_desc) values('讲师部','教书育人'),('安保部','维护治安');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from dep;
+----+-----------+--------------+
| id | dep_name  | dep_desc     |
+----+-----------+--------------+
|  1 | 讲师部    | 教书育人     |
|  2 | 安保部    | 维护治安     |
+----+-----------+--------------+
2 rows in set (0.00 sec)

mysql> insert into emp(name,dep_id) values('jason',1),('kevin',2);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from emp;
+----+-------+--------+--------+
| id | name  | gender | dep_id |
+----+-------+--------+--------+
|  1 | jason | male   |      1 |
|  2 | kevin | male   |      2 |
+----+-------+--------+--------+
2 rows in set (0.00 sec)
"""
1.创建表的时候需要先创建被关联的表(没有外键) 然后再是关联表(有外键)
2.插入表数据的时候 针对外键字段只能填写被关联表字段已经出现过的数据值
3.被关联字段无法修改和删除
	有点不太好 操作限制性太强
"""
级联更新、级联删除
	被关联数据一旦变动 关联的数据同步变动
	on update cascade  # 级联更新 
	on delete cascade  # 级联删除
级联更新、级联删除
	被关联数据一旦变动 关联的数据同步变动
create table emp1(
	id int primary key auto_increment,
 	name varchar(32),
  	gender enum('male','female','others') default 'male',
   dep_id int,
   foreign key(dep_id) references dep1(id) 
   on update cascade
   on delete cascade
);
create table dep1(
	id int primary key auto_increment,
   	dep_name varchar(32),
    dep_desc varchar(32)
);
mysql> insert into dep1(dep_name,dep_desc) values('讲师部','教书育人'),('安保部','维护治安');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0
            
mysql> insert into emp1(name,dep_id) values('jason',1),('kevin',2);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0
            
mysql> update dep1 set id=200 where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from emp1;
+----+-------+--------+--------+
| id | name  | gender | dep_id |
+----+-------+--------+--------+
|  1 | jason | male   |      1 |
|  2 | kevin | male   |    200 |
+----+-------+--------+--------+
2 rows in set (0.00 sec)
"""
扩展:
    在实际工作中 很多时候可能并不会使用外键
        因为外键增加了表之间的耦合度 不便于单独操作 资源消耗增加
    我们为了能够描述出表数据的关系 又不想使用外键
        自己通过写SQL 建立代码层面的关系
	如果表少的话使用外键会方便一点,但是如果表多的话就不建议使用外键了,那样的话就会乱
"""

表关系之多对多

以书籍表与作者表为例
	1.先站在书籍表的角度
		问:一条书籍数据能否对应多条作者数据
		答:可以
	2.再站在作者表的角度
		问:一条作者数据能否对应多条书籍数据
		答:可以
	总结:两边都可以 那么表数据关系就是'多对多'
	针对多对多表关系 外键字段不能建在任意一方!!!
mysql> create table book(
    -> id int primary key auto_increment,
    ->     title varchar(32),
    ->     author_id int,
    ->     foreign key(author_id) references author(id)
    ->     on update cascade
    ->     on delete cascade
    -> );
ERROR 1215 (HY000): Cannot add foreign key constraint
    
mysql> create table author(
    -> id int primary key auto_increment,
    ->     name varchar(32),
    ->     foreign key(book_id) references book(id)
    ->     on update cascade
    ->     on delete cascade
    -> );
ERROR 1072 (42000): Key column 'book_id' doesn't exist in table
'''需要单独开设第三张表,存储数据关系'''
create table book(
	id int primary key auto_increment,
	title varchar(32)
);
create table author(
    id int primary key auto_increment,
    name varchar(32)
);
create table book2author(
    id int primary key auto_increment,
    book_id int,
    foreign key(book_id) references book(id) 
    on update cascade 
    on delete cascade,
    author_id int,
    foreign key(author_id) references author(id) 
    on update cascade 
    on delete cascade
);

表关系之一对一

以用户表和用户详情表
	1.先站在用户表的角度
		问:一个用户可以对应多个详细信息
		答:不可以
	2.再站在用户详情表的角度
		问:一份详细信息可以对应多个用户
		答:不可以
	总结:两边都不可以,那么先考虑是不是没有关系
    	如果有关系那么肯定就是'一对一'
        针对'一对一'的表关系 外键字段建在任何一张表都可以 但是建议你建在查询频率较高的表中便于后续查询
        create table user(
            id int primary key auto_increment,
            name varchar(32)
            detail_id int unique, # 重点,给detail_id加约束条件unique
            foreign key(detail_id) references userDetail(id) 
            on update cascade
            on delete cascade
        );
        create table userDetail(
            id int primary key auto_increment,
            phone bigint
        );

小练习

判断下列表数据关系 并自定义创建出表
ps:有些表数据关系不是确定 根据具体业务可能有变化

	-------------------------------[计算机表与应用程序表]-------------------------------------
         计算机表		应用程序表
    id	computer_name		id	app_name
    1	computer1		1	weichat
    2	computer2		2	QQ
    3	computer3		3	王者荣耀
    '''
    1.站在计算机表的角度下
    	问:一台计算机可以对应多个程序
    	答:可以
    2.再站在应用程序的角度
    	问:一个应用程序可以对应多个电脑
    	答:可以
    总结:两边都可以 那么表数据关系就是'多对多'
    '''
	-------------------------------[课程表与班级表]-------------------------------------
            课程表			班级表
    id	course_name		id	class_name
    1	java			1	软件193
    2	python			2	软件191
    3	MySQL			3	软件192
    '''
    1.站在课程表的角度下
    	问:一门课程可以对应多个班级
    	答:可以
    2.再站在班级的角度
    	问:一个班级可以对应多个课程
    	答:可以
    总结:两边都可以 那么表数据关系就是'多对多'
    '''
mysql> create table course(
    -> id int primary key auto_increment,
    -> course_name varchar(50));
Query OK, 0 rows affected (0.05 sec)

mysql> create table classroom(
    -> id int primary key auto_increment,
    -> class_name varchar(50));
Query OK, 0 rows affected (0.05 sec)

mysql> create table class_course(    id int primary key auto_increment,    class_id int,    foreign key(class_id ) references classroom(id)     on update cascade     on delete cascade,    course_id int,    foreign key(course_id ) references course(id)     on update cascade     on delete cascade );
Query OK, 0 rows affected (0.05 sec)
mysql> insert into classroom(class_name) values('软件193'),('软件192'),('软件191');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from classroom;
+----+------------+
| id | class_name |
+----+------------+
|  1 | 软件193    |
|  2 | 软件192    |
|  3 | 软件191    |
+----+------------+
3 rows in set (0.00 sec)

mysql> insert into course(course_name) values('java'),('MySQL'),('python');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from course;
+----+-------------+
| id | course_name |
+----+-------------+
|  1 | java        |
|  2 | MySQL       |
|  3 | python      |
+----+-------------+
3 rows in set (0.00 sec)

mysql> insert into class_course(class_id,course_id) values(1,2),(2,3),(1,3);
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from class_course;
+----+----------+-----------+
| id | class_id | course_id |
+----+----------+-----------+
|  1 |        1 |         2 |
|  2 |        2 |         3 |
|  3 |        1 |         3 |
+----+----------+-----------+
3 rows in set (0.00 sec)
	-------------------------------[老师表与课程表]-------------------------------------

    	教师表					班级表
    id	student_name			id	class_name
    1	Jason				1	软件193
    2	jerry				2	软件191
    3	kerry				3	软件192
	'''
    1.站在教师表的角度下
    	问:一位老师可以对应多个班级
    	答:可以
    2.再站在班级的角度
    	问:一个班级可以对应多个老师
    	答:可以
    总结:两边都可以 那么表数据关系就是'多对多'
    '''
	书籍表与出版社表
    	书籍表			出版社表
    id	book_name		id	publish_name
    1	XXXXX			1	清华大学出版社
    2	AAAAA			2	北京大学出版设
    3	BBBBB			3	人民日报出版社
    '''
    1.站在书籍表的角度下
    	问:一本书可以对应多个出版社
    	答:可以
    2.再站在出版社的角度
    	问:一个出版社可以对应多本书
    	答:可以
    总结:两边都可以 那么表数据关系就是'多对多'
    '''
	-------------------------------[学生表与班级表]-------------------------------------
    	学生表				班级表
    id	student_name			id	class_name
    1	张小冉				1	软件193
    2	张询彬				2	软件191
    3	张海洋				3	软件192
	'''
    1.站在学生表的角度下
    	问:一名学生可以对应多个班级
    	答:不可以
    2.再站在班级的角度
    	问:一个班级可以对应多个学生
    	答:可以
    总结:完成换位思考之后得出的答案 一个可以一个不可以
		那么表关系就是"一对多"
			班级是一 学生是多
		"""针对'一对多'的关系 外键字段建在多的一方 """
    '''
	mysql> create table class(
    -> id int primary key auto_increment,
    -> className varchar(32));
Query OK, 0 rows affected (0.03 sec)

mysql> create table student(
    -> id int primary key auto_increment,
    -> studentName varchar(32),
    -> class_id int,
    -> foreign key(class_id) references class(id));
Query OK, 0 rows affected (0.06 sec)

mysql> insert into class(className) values('软件193'),('软件192'),('软件191');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> desc class;
+-----------+-------------+------+-----+---------+----------------+
| Field     | Type        | Null | Key | Default | Extra          |
+-----------+-------------+------+-----+---------+----------------+
| id        | int(11)     | NO   | PRI | NULL    | auto_increment |
| className | varchar(32) | YES  |     | NULL    |                |
+-----------+-------------+------+-----+---------+----------------+
2 rows in set (0.03 sec)

mysql> selelct * from class;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'selelct * from class' at line 1
mysql> select * from class;
+----+-----------+
| id | className |
+----+-----------+
|  1 | 软件193   |
|  2 | 软件192   |
|  3 | 软件191   |
+----+-----------+
3 rows in set (0.00 sec)

mysql> insert into student(studentName,class_id) values('张小冉',1);
Query OK, 1 row affected (0.02 sec)

mysql> select * from student;
+----+-------------+----------+
| id | studentName | class_id |
+----+-------------+----------+
|  1 | 张小冉      |        1 |
+----+-------------+----------+
1 row in set (0.00 sec)
posted @ 2022-08-16 20:04  张张包~  阅读(412)  评论(0编辑  收藏  举报