一、外键的约束
创建一个部门表dept,包括部门id,部门名字
mysql> select * from dept;
+---------+-----------+
| dept_id | dept_name |
+---------+-----------+
| 1 | 教学部 |
| 2 | 品保部 |
| 3 | 技术部 |
+---------+-----------+
创建一张员工表emp,包括员工id,员工姓名,部门id,地区时间
mysql> select * from emp;
+--------+----------+---------+---------------------+
| emp_id | emp_name | dept_id | reg_time |
+--------+----------+---------+---------------------+
| 1 | laoxing | 1 | 2023-04-10 09:39:49 |
| 2 | sl | 1 | 2023-04-10 09:39:55 |
| 3 | eb | 2 | 2023-04-10 09:40:09 |
| 4 | ej | 2 | 2023-04-10 09:40:13 |
| 5 | js | 3 | 2023-04-10 09:40:19 |
+--------+----------+---------+---------------------+
对这两张表进行增删改查发现:添加员工的时候,可以随便添加,尽管这个部门不存在;删除一个部门,部门下面的员工也还在。这些在实际开发中都是不合理的,造成不合理的原因是,这两张表是没有关系的!!!
怎么让这两张表产生关系呢?使用外键!!!
mysql> create table emp (
-> emp_id int primary key auto_increment,
-> emp_name varchar(32) ,
-> dept_id int,
-> reg_time timestamp default current_timestamp,
-> constraint fk_emp_dept foreign key(dept_id) references dept(dept_id)
-> );
#constraint 约束的意思
#fk_emp_dept 外键的名字 随意起一般是fk和关联的两个表的名字
#foreign key(dept_id) 外键 使用本表中的一个字段作为外键去和别的表产生关系
#references 关联 引用的意思,去关联主表里面的某一个字段(外键所在的表叫副表)
这时,在员工表中插入一条信息,若部门id在部门表中不存在,结果报错:
insert into emp (emp_name, dept_id) values("gd", 4);#不存在dept_id=4
在部门表中删除或者修改一条数据,也可能报错:
delete from dept where dept_id = 1;#在员工表中有与dept_id=1的员工与之对应,不能删
update dept set dept_id = 5 where dept_id = 1;#在员工表中有与dept_id=1的员工与之对应,不能改
加上外键约束以后,增删改 有时会报错 主表(部门表) 副表(员工表)
增加的时候:先看主表,然后增加副表
删除的时候:先删除副表,再删除主表
修改的时候:先修改副表,再修改主表 这样才能不报错
太麻烦了,可以借助级联操作 级联操作!!!级联修改 和 级联删除
mysql> create table emp (
-> emp_id int primary key auto_increment,
-> emp_name varchar(32) ,
-> dept_id int,
-> reg_time timestamp default current_timestamp,
-> constraint fk_emp_dept foreign key(dept_id) references dept(dept_id)
#加的两个状态
-> on delete cascade
-> on update cascade
-> );
mysql> select * from dept;
+---------+-----------+
| dept_id | dept_name |
+---------+-----------+
| 1 | 教学部 |
| 2 | 品保部 |
+---------+-----------+
mysql> select * from emp;
+--------+----------+---------+---------------------+
| emp_id | emp_name | dept_id | reg_time |
+--------+----------+---------+---------------------+
| 3 | xueke | 2 | 2023-04-10 10:46:04 |
| 4 | erbei | 2 | 2023-04-10 10:46:08 |
| 5 | erjia | 2 | 2023-04-10 10:46:11 |
+--------+----------+---------+---------------------+
这时再对部门表进行删除操作:
delete from dept where dept_id =1;
#删除了部门表中与dept_id=1相关信息,查看员工表,与部门表dept_id=1对应的员工也删除了
mysql> select * from emp;
+--------+----------+---------+---------------------+
| emp_id | emp_name | dept_id | reg_time |
+--------+----------+---------+---------------------+
| 3 | xueke | 2 | 2023-04-10 10:46:04 |
| 4 | erbei | 2 | 2023-04-10 10:46:08 |
| 5 | erjia | 2 | 2023-04-10 10:46:11 |
+--------+----------+---------+---------------------+
对部门表进行修改:
update dept set dept_id = 4 where dept_id =2;
#修改部门表dept_id=1为dept_id=4,查看员工表,与部门表dept_id=1对应的员工也修改为dept_id=4了
mysql> select * from emp;
+--------+----------+---------+---------------------+
| emp_id | emp_name | dept_id | reg_time |
+--------+----------+---------+---------------------+
| 3 | xueke | 4 | 2023-04-10 10:46:04 |
| 4 | erbei | 4 | 2023-04-10 10:46:08 |
| 5 | erjia | 4 | 2023-04-10 10:46:11 |
+--------+----------+---------+---------------------+
外键约束和增删改有关 查询无关
真实开发的时候,特别是外键约束的表的时候,要加上级联删除和级联修改!!!
二、联表查询
2.1常规的联表查询
mysql> select * from dept;
+---------+-----------+
| dept_id | dept_name |
+---------+-----------+
| 4 | 品保部 |
| 5 | 教学部 |
| 6 | 技术部 |
+---------+-----------+
mysql> select * from emp;
+--------+----------+---------+---------------------+
| emp_id | emp_name | dept_id | reg_time |
+--------+----------+---------+---------------------+
| 3 | xueke | 4 | 2023-04-10 10:46:04 |
| 4 | erbei | 4 | 2023-04-10 10:46:08 |
| 5 | erjia | 4 | 2023-04-10 10:46:11 |
| 6 | 东哥 | 5 | 2023-04-10 11:00:59 |
| 7 | 飞哥 | 5 | 2023-04-10 11:01:11 |
+--------+----------+---------+---------------------+
联表查询 查看 erbei 属于哪个部门:
mysql> select emp.emp_name, dept.dept_name#查什么
-> from emp, dept#从哪查
-> where emp.dept_id = dept.dept_id and emp.emp_name = "erbei";#查询条件
+----------+-----------+
| emp_name | dept_name |
+----------+-----------+
| erbei | 品保部 |
+----------+-----------+
对表起表明:
mysql> select e.emp_name as "员工名字", d.dept_name as "部门名字"
-> from emp e, dept d
-> where e.dept_id = d.dept_id and e.emp_name = "erbei";
+----------+-----------+
| emp_name | dept_name |
+----------+-----------+
| erbei | 品保部 |
+----------+-----------+
找出教学部有哪些员工:
mysql> select d.dept_name, e.emp_name
-> from dept d, emp e
-> where d.dept_id = e.dept_id and d.dept_name= "教学部";
+-----------+----------+
| dept_name | emp_name |
+-----------+----------+
| 教学部 | 东哥 |
| 教学部 | 飞哥 |
+-----------+----------+
2.2内连接与外连接
2.2.1内连接
inner join 与join 是相同的。
查看erjia在哪个部门:
mysql> select e.emp_name, d.dept_name #查什么
-> from emp e #从第一个表
-> inner join dept d #关联第二表
-> on e.dept_id = d.dept_id #表之间的连接的字段
-> where e.emp_name = "erjia"; #条件
+----------+-----------+
| emp_name | dept_name |
+----------+-----------+
| erjia | 品保部 |
+----------+-----------+
查看品保部有哪些员工:
mysql> select d.dept_name, e.emp_name
-> from dept d
-> join emp e
-> on d.dept_id = e.dept_id
-> where d.dept_name= "品保部";
+-----------+----------+
| dept_name | emp_name |
+-----------+----------+
| 品保部 | xueke |
| 品保部 | erbei |
| 品保部 | erjia |
+-----------+----------+
2.2.2外连接【开发中用的较少】
外连接分为 左外连接 和 右外连接
LEFT OUTER JOIN 关键字会从左表 (table_name1) 那里返回所有的行,即使在右表 (table_name2) 中没有匹配的行。
mysql> select *
-> from dept d
-> left outer join emp e
-> on d.dept_id = e.dept_id;
+---------+-----------+--------+----------+---------+---------------------+
| dept_id | dept_name | emp_id | emp_name | dept_id | reg_time |
+---------+-----------+--------+----------+---------+---------------------+
| 4 | 品保部 | 3 | xueke | 4 | 2023-04-10 10:46:04 |
| 4 | 品保部 | 4 | erbei | 4 | 2023-04-10 10:46:08 |
| 4 | 品保部 | 5 | erjia | 4 | 2023-04-10 10:46:11 |
| 5 | 教学部 | 6 | 东哥 | 5 | 2023-04-10 11:00:59 |
| 5 | 教学部 | 7 | 飞哥 | 5 | 2023-04-10 11:01:11 |
| 6 | 技术部 | NULL | NULL | NULL | NULL |
| 7 | 总经办 | NULL | NULL | NULL | NULL |
+---------+-----------+--------+----------+---------+---------------------+
RIGHT OUTER JOIN 关键字会从右表 (table_name2) 那里返回所有的行,即使在左表 (table_name1) 中没有匹配的行。
mysql> select *
-> from emp e
-> right outer join dept d
-> on e.dept_id = d.dept_id;
+--------+----------+---------+---------------------+---------+-----------+
| emp_id | emp_name | dept_id | reg_time | dept_id | dept_name |
+--------+----------+---------+---------------------+---------+-----------+
| 3 | xueke | 4 | 2023-04-10 10:46:04 | 4 | 品保部 |
| 4 | erbei | 4 | 2023-04-10 10:46:08 | 4 | 品保部 |
| 5 | erjia | 4 | 2023-04-10 10:46:11 | 4 | 品保部 |
| 6 | 东哥 | 5 | 2023-04-10 11:00:59 | 5 | 教学部 |
| 7 | 飞哥 | 5 | 2023-04-10 11:01:11 | 5 | 教学部 |
| NULL | NULL | NULL | NULL | 6 | 技术部 |
| NULL | NULL | NULL | NULL | 7 | 总经办 |
+--------+----------+---------+---------------------+---------+-----------+
三、多表联查在开发中的使用
3.1一对多或多对一
场景:一个老师教多个学生
mysql> create table teacher (
-> t_id int primary key auto_increment,
-> t_name varchar(32)
-> );
mysql> create table student(
-> s_id int primary key auto_increment,
-> s_name varchar(32),
-> teacher_id int
-> );
mysql> select * from teacher;
+------+--------+
| t_id | t_name |
+------+--------+
| 1 | 王博 |
| 2 | 老邢 |
| 3 | 扫雷 |
+------+--------+
mysql> select * from student;
+------+--------+------+
| s_id | s_name | t_id |
+------+--------+------+
| 1 | 文博 | 1 |
| 2 | 开放 | 1 |
| 3 | 狗蛋 | 2 |
| 4 | 张三 | 3 |
| 5 | 李四 | 2 |
| 6 | 王五 | 3 |
| 7 | 赵六 | 1 |
| 8 | 嬴政 | 1 |
+------+--------+------+
查看老邢带的学生有哪些:
#方法一:常规联表查询
mysql> select teacher.t_name, student.s_name
-> from teacher, student
-> where teacher.t_id = student.teacher_id and teacher.t_name = "老邢";
#方法二:内连接联表查询
mysql> select t.t_name,s.s_name
-> from teacher t
-> inner join student s
-> on t.t_id = s.teacher_id
-> where t.t_name = "老邢";
+--------+--------+
| t_name | s_name |
+--------+--------+
| 老邢 | 狗蛋 |
| 老邢 | 李四 |
+--------+--------+
3.2多对多
场景: 一个学生对应着多门课程 一个课程可以被多个学生选择
mysql> create table stu (#学生表
-> s_id int primary key auto_increment,
-> s_name varchar(32)
-> );
mysql> create table course (#课程表
-> c_id int primary key auto_increment,
-> c_name varchar(32)
-> );
mysql> create table stu_course (#中间表
-> id int primary key auto_increment,
-> s_id int,
-> c_id int
-> );
有三张表就意味着着必须使用三表联查
司马懿选了哪些课程:
#方法一:常规联表查询
mysql> select stu.s_name, course.c_name
-> from stu, stu_course, course
-> where stu.s_id = stu_course.s_id and stu_course.c_id = course.c_id and stu.s_name = "司马懿";
#方法二:内连接联表查询
mysql> select s.s_name, c.c_name
-> from stu s
-> inner join stu_course sc
-> on s.s_id = sc.s_id
-> inner join course c
-> on sc.c_id = c.c_id
-> where s.s_name = "司马懿";
+------+-----------+----+------+------+------+--------+
| s_id | s_name | id | s_id | c_id | c_id | c_name |
+------+-----------+----+------+------+------+--------+
| 1 | 司马懿 | 1 | 1 | 1 | 1 | 物理 |
| 1 | 司马懿 | 2 | 1 | 3 | 3 | Java |
| 1 | 司马懿 | 3 | 1 | 4 | 4 | python |
+------+-----------+----+------+------+------+--------+
挖掘机被哪些人选了
mysql> select c.c_name, s.s_name
-> from course c
-> inner join stu_course sc
-> on c.c_id = sc.c_id
-> inner join stu s
-> on sc.s_id = s.s_id
-> where c.c_name = "挖掘机";
+-----------+-----------+
| c_name | s_name |
+-----------+-----------+
| 挖掘机 | 孙悟空 |
| 挖掘机 | 林黛玉 |
+-----------+-----------+