MySQL联合查询(多表查询)知识点总结
范式设计后数据拆分到多张表,想要拿到完整业务数据就需要联合查询,包含:内连接、外连接、自连接、子查询、合并查询、插入查询结果。
一、联合查询底层原理:笛卡尔积
- 多表查询会先做笛卡尔积,把A表每一行和B表每一行全部组合,生成巨大临时结果集。
- 再通过连接条件过滤无效组合,留下符合关联关系的数据。
- ⚠️注意:参与表越多、数据量越大,笛卡尔积结果集会爆炸,性能变差,尽量少关联过多表。
实操技巧:给表起别名简化书写;多表存在同名字段,必须使用
表名.列名消除歧义。
测试用表说明
用到四张表:
student学生表、class班级表、course课程表、score成绩表。
二、表连接分类
1. 内连接 INNER JOIN
只返回两张表满足连接条件的记录 ,两边匹配不到的数据不会出现在结果。
两种等价写法:
sql
-- 写法1:逗号 + where
select 字段 from 表1,表2 where 连接条件 and 其他条件;
-- 写法2:JOIN ... ON(推荐,连接条件和普通过滤条件分离)
select 字段 from 表1 inner join 表2 on 连接条件 where 其他条件;
示例:查询唐三藏所有考试成绩;多表联查学生+班级+课程+成绩;内连接不会出现没有成绩的学生。
2. 外连接
MySQL支持左外连接、右外连接,不支持全外连接
| 类型 | 说明 | 语法 |
|---|---|---|
左外连接 LEFT JOIN |
保留左表全部行 ,右表匹配不到填充NULL |
select * from A left join B on 连接条件; |
右外连接 RIGHT JOIN |
保留右表全部行 ,左表匹配不到填充NULL |
select * from A right join B on 连接条件; |
典型案例:
- 左连接:查询没有参加考试的学生 ,学生左表,成绩右表;判断成绩字段
IS NULL筛选无成绩学生。 - 右连接:查询没有学生的班级,班级放join右侧,学生放左侧,学生字段为NULL即为空班级。
小技巧:左连接可以改写为右连接,开发优先习惯用左连接。
⚠️外连接关联条件写在ON后,不要写在WHERE,写where会退化成内连接。
3. 自连接
一张表自己和自己做连接 ,必须给同一张表起两个不同别名 ,否则报错。
用途:实现行与行之间对比 。
示例:同一个学生,比较MySQL课程成绩和Java课程成绩,把score表起两个别名s1、s2做关联。
三、子查询(嵌套查询)
子查询:将一条select的查询结果,当做另一条SQL的条件/临时表。分为四类:
-
单行子查询 :子查询返回1行结果,使用
=判断。sqlselect * from student where class_id = (select class_id from student where name='不想毕业'); -
多行子查询 :子查询返回多条,使用
IN / NOT INsqlselect * from score where course_id in (select id from course where name='Java' or name='MySQL'); -
多列子查询 :子查询返回多列,外层where括号内写多列匹配
(col1,col2) IN (...),适合查找重复记录。 -
FROM后的子查询(派生表/临时表) :把子查询结果当成一张临时表,必须给临时表起别名。
sql
select * from score s, (子查询SQL) tmp where s.score > tmp.score;
注意:子查询可以多层嵌套,复杂SQL可读性变差,复杂场景优先改用表连接。
四、合并查询 UNION / UNION ALL
把多条SELECT结果集上下拼接 ,要求前后select返回列数量、类型一致,列名以第一条select为准。
UNION:合并并且自动去重,消耗性能;UNION ALL:直接拼接,不去重,保留全部数据,性能更高;
业务如果不需要去重优先用
UNION ALL。
示例:学生表id<3的数据 union 另一张student1全部数据。
五、插入查询结果
语法:INSERT INTO 目标表(列...) SELECT ...
把select查询出来的结果,直接插入目标表,常用于数据复制、迁移。
sql
insert into student1(name,sno,age,gender,enroll_date,class_id)
select s.name, s.sno, s.age, s.gender, s.enroll_date, s.class_id
from student s join class c on s.class_id=c.id where c.name='C++001班';
六、核心易错点整理
- 笛卡尔积风险:多表必须写连接条件,忘记条件会产生海量无效数据;
- 字段重名:多表同名字段必须加表别名限定,否则报
Column ... is ambiguous; - 外连接:匹配条件写ON,业务过滤写WHERE,不要把外连接匹配条件挪到where;
- from后面的子查询,临时表必须写别名,语法强制;
- UNION和UNION ALL区别:UNION自动去重,性能差;UNION ALL直接拼接;
- 自连接:同一张表做连接,必须设置两个不同别名;
- IN子查询注意NULL:子查询结果存在NULL时,
NOT IN查询结果为空,尽量避免。