MySQL之外键介绍

引言

表与表之间建关系

定义一张员工表,表中有很多字段
id、name、gender、dep_name、dep_desc
# 1. 该表的组织结构不是很清晰(可忽视)
# 2. 浪费硬盘空间(可忽视)
# 3. 数据的扩展性极差(无法忽视)
# 如何优化?
'''上述问题就类似于你将所有的代码都写在了一个py文件中'''
解决方案:将员工表拆分为部门表

外键(foreign key)

外键就是用来帮助我们建立表与表之间关系的。在了解表关系之前我们先要直到级联更新和级联删除这两个概念。

级联更新和级联删除

  • 添加级联更新和级联删除时需要在外键约束后面添加
  • 在删除父表中的数据时,级联删除子表中的数据 on delete cascade
  • 在更新父表中的数据时,级联更新子表中的数据 on update cascade
  • 以上的级联更新和级联删除谨慎使用,因为级联操作会将数据改变或删除【数据无价】
  • 在修改约束条件时,建议可以将原先的约束删除再重新添加约束条件

使用

  • 删除外键约束
alter table t_student drop foreign key t_student_classno_fk;
  • 添加级联删除的外键约束
alter table t_student add constraint t_student_classno_fk foreign key(classno) references t_class(cno) on delete cascade;
  • 添加级联更新的外键约束
alter table t_student add constraint t_student_classno_fk foreign key(classno) references t_class(cno) on update cascade;

表关系

表与表之间最多只有四种关系
一对多关系
在MySQL的关系中没有多对一这么说的,一对多,多对一都叫一对多!!
多对多关系
一对一关系
没有关系

一对多关系

image

判断表与表之间关系的时候,前期不熟悉的情况下,一定要按照换位思考的思想来看,分别站在两张标的角度考虑
先站在员工表
思考一个员工能否对应对个部门(一个员工数据能否对应多条部门数据)
不能(不能直接得出结论,一定要两张表都要考虑完全)
再站在部门表
思考一个部门能否对应多个员工(一个部门数据能否对应多条员工数据)
然后得出结论
员工表和部门表为单向的一对多关系
所以表关系就是一对多
foreign key
1. 一对多关系 外键字段建立在多的一方
2. 在创建表的时候,一定要先建被关联表,因为被关联表中没有外键约束
3. 在录入数据的时候,也必须先录入被关联表
# SQL语句建立表关系
create table dep(
id int primary key auto_increment,
dep_name char(16),
dep_desc char(32)
);
create table emp(
id int primary key auto_increment,
name char(16),
gender enum('male','female','others'),
dep_id int,
foreign key(dep_id) references dep(id)
# 首先声明dep_id是外键字段,其次表明跟哪张表的哪个字段有关系
);
insert into emp(name,dep_id) values('xiao',1);
# ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`day02`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`dep_id`) REFERENCES `dep` (`id`))
所以先插部门数据
insert into dep(dep_name,dep_desc) values ('sb教学部','教书与人'),('外交部','多人外交'),('nb技术部','技术能力有限部门');
insert into emp(name,dep_id) values('xiao',3),('quan',1),('zheng',2);

image

# 删除emp里面的dep_id字段或者修改dep表里面的id字段(不行)
update dep set id=200 where id=2;
# ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`day02`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`dep_id`) REFERENCES `dep` (`id`))
delete from emp;
# 1. 先删除教学部对应的员工数据,之后再删除部门
操作太过繁琐
# 2. 真正做到数据之间有关系
更新就同步数据
删除就同步删除
"""
级联更新
级联删除
"""
create table dep(
id int primary key auto_increment,
dep_name char(16),
dep_desc char(32)
);
create table emp(
id int primary key auto_increment,
name char(16),
gender enum('male','female','others'),
dep_id int,
foreign key(dep_id) references dep(id)
on update cascade # 同步更新
on delete cascade # 同步删除
);
insert into dep(dep_name,dep_desc) values ('sb教学部','教书与人'),('外交部','多人外交'),('nb技术部','技术能力有限部门');
insert into emp(name,dep_id) values('xiao',3),('quan',1),('zheng',2);
select * from dep;
+----+-------------+--------------------------+
| id | dep_name | dep_desc |
+----+-------------+--------------------------+
| 1 | sb教学部 | 教书与人 |
| 2 | 外交部 | 多人外交 |
| 3 | nb技术部 | 技术能力有限部门 |
+----+-------------+--------------------------+
select * from emp;
+----+-------+--------+--------+
| id | name | gender | dep_id |
+----+-------+--------+--------+
| 1 | xiao | NULL | 3 |
| 2 | quan | NULL | 1 |
| 3 | zheng | NULL | 2 |
+----+-------+--------+--------+
update dep set id=200 where id=2;
select * from dep;
+-----+-------------+--------------------------+
| id | dep_name | dep_desc |
+-----+-------------+--------------------------+
| 1 | sb教学部 | 教书与人 |
| 3 | nb技术部 | 技术能力有限部门 |
| 200 | 外交部 | 多人外交 |
+-----+-------------+--------------------------+
select * from emp;
+----+-------+--------+--------+
| id | name | gender | dep_id |
+----+-------+--------+--------+
| 1 | xiao | NULL | 3 |
| 2 | quan | NULL | 1 |
| 3 | zheng | NULL | 200 |
+----+-------+--------+--------+
delete from dep where id=1;
select * from dep;
+-----+-------------+--------------------------+
| id | dep_name | dep_desc |
+-----+-------------+--------------------------+
| 3 | nb技术部 | 技术能力有限部门 |
| 200 | 外交部 | 多人外交 |
+-----+-------------+--------------------------+
select * from emp;
+----+-------+--------+--------+
| id | name | gender | dep_id |
+----+-------+--------+--------+
| 1 | xiao | NULL | 3 |
| 3 | zheng | NULL | 200 |
+----+-------+--------+--------+

多对多关系

图书表和作者表
create table book(
id int primary key auto_increment,
title varchar(32),
price int,
author_id int,
foreign key(author_id) references author(id)
on update cascade
on delete cascade
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int,
book_id int,
foreign key(book_id) references book(id)
on update cascade
on delete cascade
);
"""
按照上述的方式创建,一个都不会成功
其实我们只是想记录书籍和作者的关系
针对多对多字段表关系,不能在两张表原有的表中创建外键
需要你单独再开设一张专门用来存储两张表数据之间的关系
"""

image

image

create table book(
id int primary key auto_increment,
title varchar(32),
price int
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int
);
insert into book(title,price) values('java',10000),('python',20000),('go',15000);
insert into author(name,age) values('xiao',18),('quan',77);
create table book2author(
id int primary key auto_increment,
author_id int,
book_id int,
foreign key(author_id) references author(id)
on update cascade
on delete cascade,
foreign key(book_id) references book(id)
on update cascade
on delete cascade
);
desc book2author;
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| author_id | int(11) | YES | MUL | NULL | |
| book_id | int(11) | YES | MUL | NULL | |
+-----------+---------+------+-----+---------+----------------+
insert into book2author(author_id,book_id) values(1,1),(1,2),(2,3);
select * from book2author;
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
+----+-----------+---------+
delete from author where id = 1;
select * from book;
+----+--------+-------+
| id | title | price |
+----+--------+-------+
| 1 | java | 10000 |
| 2 | python | 20000 |
| 3 | go | 15000 |
+----+--------+-------+
select * from book2author;
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 3 | 2 | 3 |
+----+-----------+---------+
通过第三张表来建立两张表之间的联系

一对一关系

"""
id name age addr phone hobby email
如果一个表的字段特别多,每次查询又不是所有的字段都用的到
就可以将表一分为二
例如:
用户表 id name age
用户详情表 id addr phone hobby email
站在用户表
一个用户能否对应多个用户详情 不能!!!
站在用户详情表
一个详情表能否属于多个用户 不能!!!
结论:单向的一对多都不成立,那么这个时候两者之间的表关系就是一对一或者是没关系
"""
"""
客户表和学生表
在报名之前是客户
报名之后是学生,不是所有客户都能成为学生
"""
一对一 外键字段建在任意一方都可以,但是推荐你建在查询频率比较高的表中
create table authordetail(
id int primary key auto_increment,
phone int,
addr varchar(32)
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int,
authordetail_id int unique,
foreign key(authordetail_id) references authordetail(id)
on update cascade
on delete cascade
);

总结

  • 表关系的建立需要用到foreign key

    • 一对多
      外键字段建在多的一方
    • 多对多
      自己开设第三张表存储
    • 一对一
      建在任意一方都可以,但是推荐你建在查询频率比较高的表中
  • 判断表之间关系的方式:换位思考

    • 员工与部门
    • 图书与作者
    • 作者与作者详情
  • 删除带有外键约束的表

    • 先删除关联表
    • 再删除被关联表
posted @   Xiao0101  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示

目录