一、联合查询的作用
由于范式的规则,数据分到多个表中,想要查询完整的信息,就需要联合查询多张表。比如查询学生的学生信息和所在班级的信息,就需要联合查询学生表和班级表。
二、联合查询过程
案例:查询学生姓名为孙悟空的详细信息,包括学学个人信息和班级信息。
# 课程表
insert into course (name) values ('Java'), ('C++'), ('MySQL'), ('操作系统'), ('计算机网络'), ('数据结构');
# 班级表
insert into class(name) values ('Java001班'), ('C++001班'), ('前端001班');
# 学生表
insert into student (name, sno, age, gender, enroll_date, class_id) values
('唐三藏', '100001', 18, 1, '1986-09-01', 1),
('孙悟空', '100002', 18, 1, '1986-09-01', 1),
('猪悟能', '100003', 18, 1, '1986-09-01', 1),
('沙悟净', '100004', 18, 1, '1986-09-01', 1),
('宋江', '200001', 18, 1, '2000-09-01', 2),
('武松', '200002', 18, 1, '2000-09-01', 2),
('李逹', '200003', 18, 1, '2000-09-01', 2),
('不想毕业', '200004', 18, 1, '2000-09-01', 2);
# 成绩表
insert into score (score, student_id, course_id) values
(70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6),
(60, 2, 1),(59.5, 2, 5),(33, 3, 1),(68, 3, 3),
(99, 3, 5),(67, 4, 1),(23, 4, 3),(56, 4, 5),
(72, 4, 6),(81, 5, 1),(37, 5, 5),(56, 6, 2),
(43, 6, 4),(79, 6, 6),(80, 7, 2),(92, 7, 6);
1、确定联合查询的表
学生表 student 和班级表 class,进行笛卡尔积:
2、过滤掉班级号不匹配的信息
学生表和班级表都有 id,需要指定是哪个表的 id。
3、筛选出指定学生
4、筛选出想要的字段,并精简表名
三、内连接
上面的示例就是内连接。
1、语法
select 字段 from 表1 别名1, 表2 别名2 where 连接条件 and 其他条件;
select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 where 其他条件;
2、示例1:查询“唐三藏”同学的成绩。
查询的表:student、score。
确定连接条件需要查看表结构:s.id = sc.student_id。
其他条件:s.name = '唐三藏'。
字段:s.name、sc.score。
3、示例2:查询所有同学的总成绩、个人信息。
查询的表:student、score。
连接条件:s.id = sc.student_id。
分组查询:sc.student_id,① 唯一。② 对哪个表的列求和,就对那个表的列进行分组。
查询字段:s.id、s.sno、s.name、s.age、s.gender、s.enroll_date、sc.s(分组后,这些字段的值对于某一组都是确定的,可以在分组后查询)、avg(sc.score)(聚合函数的值也是确定的)。
查询结果:
4、示例3:查询所有同学的个人信息、每门课的成绩
查询表:student、score、course。
连接条件:
查询结果:
也可以使用:
工作中尽量少对大表关联查询,最多关联 3 个表,因为很耗资源。
四、外连接
- 左外连接:以左表为基准,显示左表所有值,显示右表匹配的值,没有匹配的就显示 NULL。
- 右外连接:跟左外连接反着来。
- 全连接:左、右都全显示,没有匹配的就显示 NULL。(MySQL 不支持)
内连接是有匹配的才显示,外连接没有匹配的也会显示 NULL。
1、语法
-- 左外连接,表1完全显⽰
select 字段 from 表名1 left join 表名2 on 连接条件;
-- 右外连接,表2完全显⽰
select 字段 from 表名1 right join 表名2 on 连接条件;
2、示例1:查询没有参加考试的同学信息
该查询就无法使用内连接完成。
查询的表:student、score。
连接条件:s.id = sc.student_id。
没有参加考试的同学:同学信息要全显示,没有匹配的成绩显示 NULL。
其它条件:sc.score is null。(注意不要用 =,null 与任意值做计算都是 null)
3、示例2:查询没有学生的班级
五、自连接
自连接是自己跟自己做笛卡尔积。上面的连接都是列与列的比较,有时候我们想要同一列中行与行的比较,但MySQL 又不支持直接比较行与行。就需要用自连接,把行转为列。
比如我们想要比较同一个学生的 score 字段的不同课程成绩:
1、示例1:显示所有 'Java' 比 'MySQL' 分数低的学生和成绩信息
查询的表:score sc1、score sc2、student s。
连接条件:sc1.student_id = sc2.student_id。同一个学生的成绩进行比较。(自连接)
sc1.student_id = student.id。
其它条件:sc1.class_id = Java 的编号,sc2.class_id = MySQL 的编号。(选出对应的课程)。sc1.score < sc2.score。
2、示例2:通过课程名实现示例1
有时候希望实现用户输入 2 个课程名称,就能直接查询的功能。所以先把 score 表与 class 表关联后,在对课程名进行筛选。
查询的表:score sc1、class c1、score sc2、class c2。
连接条件:sc1.class_id = c1.id(代表课程1)
sc2.class_id = c2.id(代表课程2)
sc1.student_id = sc2.student_id(代表同一个学生)
其他条件:c1.name = 'Java', c2.name = 'MySQL'
sc1.score < sc2.score
六、子查询
子查询就是一个 select 语句的结果是另一个 select 语句的条件,可以嵌套多层,也叫嵌套查询。子查询只是把多条的查询语句合并成一条查询语句,在工作中不能嵌套太多,影响查询效率。
1、语法
in 表示内层的 select 返回的是一个结果集。
2、单行子查询。
内层只返回一行数据。
示例:查询 '不想毕业' 的同班同学
先要查询 '不想毕业' 是哪个班的(内层返回1行),然后根据班级 id 查询同班同学(外层)。最后排除 '不想毕业' 的信息。
3、多行子查询
内层返回多行数据。
示例:查询 'Java' 或 'MySQL' 的成绩信息。
先查询 'Java' 或 'MySQL' 的课程 id(返回2行),然后根据课程 id 查询成绩信息。
4、多列子查询
内层返回多列数据,外层条件与内层查询的列要匹配。
示例:查询重复录入的分数。
首先查询重复的成绩信息(分数、学生 id、课程 id,相同的分为一组,统计每组数据行数,计数大于1的则重复),再根据内层的查询结果在外层查询完整的成绩信息(成绩 id,分数、学生 id、课程 id)。
5、在 from 子句中使用子查询
from 后面接查询的表,这个表可以是真实表或者临时表,而子查询的结果就是存储在临时表中。
示例:查询大于 'Java001' 班平均分的成绩信息。
先查找班级为 'Java001班' 的学生成绩,即将 class、score、student 关联,选出 class.name = 'Java001班' 的数据行,再求平均值,平均值存储在临时表中。
再将平均值临时表与 score 表进行内连接,最后使用其它条件 score > avg。
七、合并查询
想让多个 select 查询结果集一起返回,就用合并查询。
1、union(结果集去重)
准备一个与 student 结构一致的 student2 表:
插入数据:
insert into student2 (name, sno, age, gender, enroll_date, class_id) values
('唐三藏', '100001', 18, 1, '1986-09-01', 1),
('刘备', '300001', 18, 1, '1993-09-01', 3),
('张飞', '300002', 18, 1, '1993-09-01', 3),
('关羽', '300003', 18, 1, '1993-09-01', 3);
student 表数据:
查询 student 表中 id < 3 的学生和 student2 表中所有学生(因为 union 去重,所以唐三藏只有一条数据):
2、union all(结果集不去重)
3、注意事项
- 单表可以使用合并语句,但是建议使用 or。
- 合并查询时,多张表的列一定要对齐。
八、插入查询结果
插入的数据是查询的结果。
示例:将 student 表中 'C++001班' 的学生数据加入到 student2 表中。
查看 student 表中 'C++001班' 的学生:
将查询结果插入到 student2 表中(注意:不使用 values,null 表示给自增的 id 占位):
插入后的 student2: