关联关系介绍
MySQL 是一个关系型数据库, 不仅可以存储数据, 还可以维护数据与数据之间的关系——通过在数据表中添加字段建立外键约束
数据与数据之间的关联关系
分为四种:
- 一对一关联
- 一对多关联
- 多对一关联
- 多对多关联
一对一关联
人 —- 身份证 一个人只有一个身份证、一个身份证只对应一个人
学生 —- 学籍 一个学生只有一个学籍、一个学籍也对应唯一的一个学生
用户 —- 用户详情 一个用户只有一个详情、一个详情也只对应一个用户
方案 1: 主键关联 —— 两张数据表中主键相同的数据为相互对应的数据
方案 2: 唯一外键 —— 在任意一张表中添加一个字段添加外键约束与另一张表主键关联, 并且将外键列添加唯一约束
一对多与多对一
班级 —- 学生 (一对多) 一个班级包含多个学生
学生 —- 班级 (多对一) 多个学生可以属于同一个班级
图书 —- 分类 商品 —— 商品类别
方案: 在多
的一端添加外键, 与一
的一端主键进行关联
多对多关联
学生 —- 课程 一个学生可以选择多门课、一门课程也可以由多个学生选择
会员 —- 社团 一个会员可以参加多个社团、一个社团也可以招纳多个会员
方法: 额外创建一张关系表来维护多对多关联 —— 在关系表中定义两个外键, 分别与两个数据表的主键进行关联
外键约束
外键约束 —— 将一个列添加外键约束与另一张表的主键 (唯一列) 进行关联之后, 这个外键约束的列添加的数据必须要在关联的主键字段中存在
示例: 学生表和班级表
- 先创建班级表
create table classes(
class_id int primary key auto_increment,
class_name varchar(40) not null unique,
class_remark varchar(200)
);
- 创建学生表 (在学生表中添加外键与班级表的主键进行关联)
# 【⽅式一】在创建表的时候, 定义 cid 字段, 并添加外键约束
# 由于 cid 列要与 classes 表的 class_id 进⾏关联, 因此 cid 字段类型和⻓度要与 class_id 一致
create table students(
stu_num char(8) primary key,
stu_name varchar(20) not null,
stu_gender char(2) not null,
stu_age int not null,
cid int,
constraint FK_STUDENTS_CLASSES foreign key(cid) references classes(class_id)
);
#【⽅式⼆】先创建表, 再添加外键约束
create table students(
stu_num char(8) primary key,
stu_name varchar(20) not null,
stu_gender char(2) not null,
stu_age int not null,
cid int
);
# 在创建表之后, 为 cid 添加外键约束
alter table students
add constraint FK_STUDENTS_CLASSES
foreign key (cid) references classes (class_id);
# 删除外键约束
alter table students drop foreign key FK_STUDENTS_CLASSES;
- 向班级表添加班级信息
insert into classes(class_name, class_remark)values('java2104', '...');
insert into classes(class_name, class_remark)values('Java2105', '...');
insert into classes(class_name, class_remark)values('Java2106', '...');
insert into classes(class_name, class_remark)values('Python2106', '...');
select * from classes;
+----------+------------+--------------+
| class_id | class_name | class_remark |
+----------+------------+--------------+
| 1 | java2104 | ... |
| 2 | java2105 | ... |
| 3 | java2106 | ... |
| 4 | Python2105 | ... |
+----------+------------+--------------+
- 向学生表中添加学生信息
insert into students(stu_num, stu_name, stu_gender, stu_age, cid)
values('20210102', '李斯', '⼥', 20, 4);
# 添加学生时, 设置给 cid 外键列的值必须在其关联的主表 classes 的 classes_id 列存在
insert into students(stu_num, stu_name, stu_gender, stu_age, cid)
values('20210103', '王五', '男', 20, 6);
关键约束 - 级联
当学生表中存在学生信息关联班级表的某条记录时,就不能对班级表的这条记录进⾏
修改 ID
和删除
操作,如下:
select * from classes;
+----------+------------+--------------+
| class_id | class_name | class_remark |
+----------+------------+--------------+
| 1 | java2104 | ... |
| 2 | java2105 | ... |
| 3 | java2106 | ... |
| 4 | Python2105 | ... |
+----------+------------+--------------+
select * from students;
+----------+----------+------------+---------+------+
| stu_num | stu_name | stu_gender | stu_age | cid |
+----------+----------+------------+---------+------+
| 20210101 | 张三 | 男 | 20 | 2 |
| 20210102 | 李斯 | ⼥ | 20 | 4 |
| 20210103 | 王五 | 男 | 18 | 1 |
| 20210104 | 赵六 | 女 | 18 | 2 |
+----------+----------+------------+---------+------+
# 班级表中 class_id = 1 的班级信息被学生表中的记录关联了, 则不能修改 java2104 的 class_id, 并且不能删除
update classes set class_id = 5 where class_name = 'java2104';
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`db_test2`.`students`, CONSTRAINT `FK_STUDENTS_CLASS` FOREIGN KEY (`cid`) REFERENCES `classes` (`class_id`))
delete from classes where class_id = 1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`db_test2`.`students`, CONSTRAINT `FK_STUDENTS_CLASS` FOREIGN KEY (`cid`) REFERENCES `classes` (`class_id`))
如果一定要修改 java2104 的班级 ID, 该如何实现?
- 将引用 java2104 班级 id 的学生记录中的 cid 修改为 NULL
update students set cid = NULL where cid = 1;
select * from students;
+----------+----------+------------+---------+------+
| stu_num | stu_name | stu_gender | stu_age | cid |
+----------+----------+------------+---------+------+
| 20210101 | 张三 | 男 | 20 | 2 |
| 20210102 | 李斯 | ⼥ | 20 | 4 |
| 20210103 | 王五 | 男 | 18 | NULL |
| 20210104 | 赵六 | 女 | 18 | 2 |
+----------+----------+------------+---------+------+
- 再修改班级信息表中 java2104 记录的 class_id
update classes set class_id = 5 where class_name = 'java2104';
select * from classes;
+----------+------------+--------------+
| class_id | class_name | class_remark |
+----------+------------+--------------+
| 2 | java2105 | ... |
| 3 | java2106 | ... |
| 4 | Python2105 | ... |
| 5 | java2104 | ... |
+----------+------------+--------------+
- 将学生表中 cid 设置为 NULL 的记录的 cid 重新修改为 java2104 这个班级的新的 id
update students set cid = 5 where cid IS NULL;
select * from students;
+----------+----------+------------+---------+------+
| stu_num | stu_name | stu_gender | stu_age | cid |
+----------+----------+------------+---------+------+
| 20210101 | 张三 | 男 | 20 | 2 |
| 20210102 | 李斯 | ⼥ | 20 | 4 |
| 20210103 | 王五 | 男 | 18 | 5 |
| 20210104 | 赵六 | 女 | 18 | 2 |
+----------+----------+------------+---------+------+
也可使用
级联
操作来实现:
- 在添加外键时, 设置
级联修改
和级联删除
# 删除原有的外键
alter table students drop foreign key FK_STUDENTS_CLASSES;
# 重新添加外键, 并设置级联修改和级联删除
alter table students add constraint FK_STUDENTS_CLASSES
foreign key(cid) references classes(class_id) ON UPDATE CASCADE ON DELETE CASCADE;
- 测试级联修改
# 修改 java2104 的 class_id, 关联 java2104 这个班级的学⽣记录的 cid 也会同步修改
update classes set class_id = 1 where class_name = 'java2104';
# 班级信息
select * from classes;
+----------+------------+--------------+
| class_id | class_name | class_remark |
+----------+------------+--------------+
| 2 | java2105 | ... |
| 3 | java2106 | ... |
| 4 | Python2105 | ... |
| 1 | java2104 | ... |
+----------+------------+--------------+
# 学生信息
select * from students;
+----------+----------+------------+---------+------+
| stu_num | stu_name | stu_gender | stu_age | cid |
+----------+----------+------------+---------+------+
| 20210101 | 张三 | 男 | 20 | 2 |
| 20210102 | 李斯 | ⼥ | 20 | 4 |
| 20210103 | 王五 | 男 | 18 | 1 |
| 20210104 | 赵六 | 女 | 18 | 2 |
+----------+----------+------------+---------+------+
- 测试级联删除
# 删除 class_id = 1 的班级信息, 学⽣表引⽤此班级信息的记录也会被同步删除
delete from classes where class_id=1;
# 班级信息
select * from classes;
+----------+------------+--------------+
| class_id | class_name | class_remark |
+----------+------------+--------------+
| 2 | java2105 | ... |
| 3 | java2106 | ... |
| 4 | Python2105 | ... |
+----------+------------+--------------+
# 学生信息
select * from students;
+----------+----------+------------+---------+------+
| stu_num | stu_name | stu_gender | stu_age | cid |
+----------+----------+------------+---------+------+
| 20210101 | 张三 | 男 | 20 | 2 |
| 20210102 | 李斯 | ⼥ | 20 | 4 |
| 20210104 | 赵六 | 女 | 18 | 2 |
+----------+----------+------------+---------+------+