摘要:在设计表时需要考虑表和表之间的一些关系,表与表之间的关系分为三种,分别为一对一、一对多(多对一)、多对多。下面用例子介绍了这三种关系,还重点介绍了几种不同情况下对外键的创建、删除以及查看的操作,并进行了代码实现。
一对一:一张表(a表)的一条记录对应另一张表(b表)的一条记录,反过来,另一张表(b表)的一条记录只能对应该表(a表)的一条记录。
例如:
建表原则:
但实际应用不多,因为这种情况完全可以建在一张表里。
编号 | 姓名 | 身份证外键 |
---|---|---|
1 | 李四 | 1 |
2 | 王五 | 2 |
身份证编号 | 身份证号 |
---|---|
1 | 12345 |
2 | 54321 |
像这样就根本没有必要,可以合成一张表
编号 | 姓名 | 身份证 |
---|---|---|
1 | 李四 | 12345 |
2 | 王五 | 54321 |
一对多(多对一):一张表中(a表)的一条记录对应另一张表(b表)的多条记录,但反过来,另一张表(b表)的一条记录只能对应该表(a表)的一条记录。
例如:
建表原则:
学生编号 | 学生姓名 | 所属班级 |
---|---|---|
1 | 张三 | 1班 |
2 | 李四 | 2班 |
3 | 王五 | 1班 |
4 | 赵六 | 2班 |
由于学生会越来越多,保存的数据也越来越多,所属班级的重复率也会越来越高,可以分为两张表:班级表和学生表
班级表(主表)
班级编号 | 班级名称 |
---|---|
1 | 1班 |
2 | 2班 |
学生表(从表)
学生编号 | 学生姓名 | 班级编号(外键) |
---|---|---|
1 | 张三 | 1 |
2 | 李四 | 2 |
3 | 王五 | 1 |
4 | 赵六 | 2 |
学生表中有一列专门存储的班级表的主键值,那么这一列对于学生表来说叫做外键列,包含外键的表叫做外键表,外键对应的表的主键所在的表我们叫做主键表。这里使用数字代替了原来的汉字,因此存储空间也能节约不少。
-- 创建主表
create table class(
cno int primary key auto_increment,
cname varchar(32)
);
-- 创建从表
create table student(
sno int primary key auto_increment,
sname varchar(32),
s_cno int not null
);
-- 在从表上的一个字段(外键字段)添加外键约束,依赖于主表的主键(班级编号)
alter table student add constraint fk_class_student foreign key(s_cno) references class(cno);
-- 向主表添加数据
insert into class values(null, '1班'),(null, '2班');
-- 向从表添加数据
insert into student values(null, '张三', 1),(null, '李四', 2);
-- 向从表中添加主表不存在的班级号 会报错,因为违反了外键约束
insert into student values(null, '王五', 3),(null, '赵六', 4);
由于学生表添加的学生所在的班级在班级表中找不到,因此无法添加。
解决方案1:添加学生信息时,修改预添加学生的班级号
解决方案2:添加学生信息前,班级表中添加需要的班级号
解决方案3:删除外键约束
-- 删除主键中已存在的班级 会报错
delete from class where cno = 2;
因为该班级正被学生表的学生引用,因此不能删除。
解决方案1:删除该班级前,删除引用该班级的学生
解决方案2:删除外键约束
方法一:在表的创建信息中可以找到外键名;
-- 查询外键
show create table student;
方法二:在报错信息中也能找到外键名;
方法三:在mysql客户端中该表的设计表的外键选项卡中可以找到外键名
方法四:通过INFORMATION_SCHEMA.KEY_COLUMN_USAGE.CONSTRAINT_NAME字段找到外键名
SELECT C.TABLE_SCHEMA 拥有者,
C.REFERENCED_TABLE_NAME 父表名称 ,
C.REFERENCED_COLUMN_NAME 父表字段 ,
C.TABLE_NAME 子表名称,
C.COLUMN_NAME 子表字段,
C.CONSTRAINT_NAME 约束名,
T.TABLE_COMMENT 表注释,
R.UPDATE_RULE 约束更新规则,
R.DELETE_RULE 约束删除规则
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE C
JOIN INFORMATION_SCHEMA. TABLES T
ON T.TABLE_NAME = C.TABLE_NAME
JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
ON R.TABLE_NAME = C.TABLE_NAME
AND R.CONSTRAINT_NAME = C.CONSTRAINT_NAME
AND R.REFERENCED_TABLE_NAME = C.REFERENCED_TABLE_NAME
WHERE C.REFERENCED_TABLE_NAME IS NOT NULL ;
alter table student drop foreign key fk_class_student;
多对多:一张表(a表)的一条记录能够对应另一张表(b表)的多条记录,同时,另一张表(b表)的一条记录也能对应该表(a表)的多条记录。
例如:
建表规则:
学生表:
学生编号 | 学生名称 |
---|---|
1 | 张三 |
2 | 李四 |
3 | 王五 |
身份表:
身份编号 | 身份名称 |
---|---|
1 | 学生 |
2 | 课代表 |
3 | 班长 |
中间表:
学生编号(外键) | 身份编号(外键) |
---|---|
1 | 1 |
1 | 2 |
1 | 3 |
2 | 1 |
2 | 2 |
3 | 1 |
含义:张三既是学生也是课代表还是个班长;李四既是学生又是课代表;王五只有一个学生身份。
-- 创建学生表(主表)
-- 指定约束名创建主键
create table student(
sno int auto_increment,
sname varchar(32),
constraint pk_student_sno primary key(sno)
);
-- 创建身份表(主表)
create table identity(
ino int auto_increment,
iname varchar(32),
constraint pk_identity_ino primary key(ino)
);
-- 创建中间表(从表),并添加外键约束
-- 指定约束名创建外键
create table mid_stu_ide(
m_sno int not null,
m_ino int not null,
constraint fk_student_mid foreign key(m_sno) references student(sno),
constraint fk_identity_mid foreign key(m_ino) references identity(ino)
);
-- 为中间表创建联合主键(可以省略这步,创建表时忘了,表外创建一波)
alter table mid_stu_ide add constraint pk_mid_msno_mino primary key(m_sno,m_ino);
-- 向学生表中添加数据
insert into student(sname) values('张三'),('李四'),('王五');
-- 向角色表中添加数据
insert into identity(iname) values('学生'),('课代表'),('班长');
-- 向中间表中添加数据
insert into mid_stu_ide values(1,1),(1,2),(1,3),(2,1),(2,2),(3,1);
-- 查看学生和职务的对应关系
select sname,iname
from student s, identity i, mid_stu_ide m
where s.sno = m.m_sno and i.ino = m.m_ino
order by sname;
-- 删除中间表的数据
-- 取消张三的课代表身份
delete from mid_stu_ide where m_sno = 1 and m_ino=2;
-- 向中间表添加不存在的学生或身份的数据
insert into mid_stu_ide values (100,100);
因为中间表在插入时,会去学生表和职务表中找对应主键,但学生表和职务表中找不到对应的数据,因此会报错。
-- 删除学生表或角色表中的数据
delete from student where sno=1;
-- 解除外键约束
alter table mid_stu_ide drop foreign key fk_student_mid;
alter table mid_stu_ide drop foreign key fk_identity_mid;