【MySQL】表的内连接和外连接详解(带图解思路 + 代码示例 + 面试常考点)
在MySQL中,JOIN 是多表查询的核心操作,主要分为内连接(INNER JOIN) 和 外连接(OUTER JOIN) 两大类。外连接又细分为 LEFT、RIGHT、FULL 三种。
一句话记住本质区别:
- 内连接:只取两个表都匹配上的记录(交集)
- 外连接:至少保留其中一张表的所有记录,匹配不上的用 NULL 填充
1. 经典 Venn 图理解(强烈建议脑补或画出来)
想象两个圆圈 A 表(左)和 B 表(右):
- INNER JOIN:只取 A ∩ B(两个圆重叠的部分)
- LEFT JOIN:取 A 全部 + A ∩ B(左圆全要,重叠部分也取,右圆只取重叠)
- RIGHT JOIN:取 B 全部 + A ∩ B(右圆全要)
- FULL OUTER JOIN:取 A ∪ B(两个圆全部,包括不重叠的部分)
MySQL 不支持 直接的 FULL OUTER JOIN,但可以用 LEFT + RIGHT + UNION 模拟。
2. 准备示例表(经典学生-课程表)
-- 学生表(左表)
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(20)
);
INSERT INTO students VALUES
(1, '张三'),
(2, '李四'),
(3, '王五'), -- 没选课
(4, '赵六'); -- 没选课
-- 选课表(右表)
CREATE TABLE courses (
student_id INT,
course_name VARCHAR(20)
);
INSERT INTO courses VALUES
(1, '数学'),
(1, '英语'),
(2, '物理'),
(5, '化学'); -- student_id=5 不存在于学生表
3. 各种 JOIN 的 SQL + 结果解释
(1) INNER JOIN(内连接,默认就是这个)
SELECT s.name, c.course_name
FROM students s
INNER JOIN courses c
ON s.id = c.student_id;
结果(只返回匹配的):
| name | course_name |
|---|---|
| 张三 | 数学 |
| 张三 | 英语 |
| 李四 | 物理 |
→ 只显示有选课的学生,王五、赵六没出现;5号课程也没出现。
等价写法(老式隐式内连接):
SELECT s.name, c.course_name
FROM students s, courses c
WHERE s.id = c.student_id;
(2) LEFT JOIN / LEFT OUTER JOIN(左外连接,最常用)
SELECT s.name, c.course_name
FROM students s
LEFT JOIN courses c
ON s.id = c.student_id;
结果:
| name | course_name |
|---|---|
| 张三 | 数学 |
| 张三 | 英语 |
| 李四 | 物理 |
| 王五 | NULL |
| 赵六 | NULL |
→ 左表(students)全部保留,没匹配的课程显示 NULL。
面试常问:LEFT JOIN 后 WHERE 放 ON 还是 WHERE 区别很大!
- ON 条件:过滤连接前的匹配
- WHERE 条件:过滤连接后的结果(可能把 LEFT 的 NULL 行干掉)
错误示范(想保留左表全部却加了 WHERE c.course_name IS NOT NULL → 变内连接了)。
(3) RIGHT JOIN / RIGHT OUTER JOIN(右外连接)
SELECT s.name, c.course_name
FROM students s
RIGHT JOIN courses c
ON s.id = c.student_id;
结果:
| name | course_name |
|---|---|
| 张三 | 数学 |
| 张三 | 英语 |
| 李四 | 物理 |
| NULL | 化学 |
→ 右表(courses)全部保留,没匹配的学生显示 NULL。
注意:LEFT 和 RIGHT 可以互相转换(把表顺序换一下 + 改 JOIN 类型),实际开发中 LEFT JOIN 占 90%,RIGHT 很少用。
(4) FULL OUTER JOIN(全外连接)—— MySQL 不原生支持
模拟写法(LEFT + RIGHT + 去重):
SELECT s.name, c.course_name
FROM students s
LEFT JOIN courses c ON s.id = c.student_id
UNION
SELECT s.name, c.course_name
FROM students s
RIGHT JOIN courses c ON s.id = c.student_id
WHERE s.id IS NULL; -- 避免重复
结果(并集):
| name | course_name |
|---|---|
| 张三 | 数学 |
| 张三 | 英语 |
| 李四 | 物理 |
| 王五 | NULL |
| 赵六 | NULL |
| NULL | 化学 |
4. 快速对比表(面试/刷题必背)
| JOIN 类型 | 返回内容 | 保留哪张表全部记录 | 没匹配的字段 | MySQL 支持 | 使用场景 |
|---|---|---|---|---|---|
| INNER JOIN | 只匹配上的(A ∩ B) | 无 | 无(不出现) | 是 | 必须双方都有数据 |
| LEFT JOIN | 左表全部 + 匹配的 | 左表 | 右表字段=NULL | 是 | 找左表所有 + 右表补充 |
| RIGHT JOIN | 右表全部 + 匹配的 | 右表 | 左表字段=NULL | 是 | 找右表所有 + 左表补充(少用) |
| FULL OUTER JOIN | 左右表全部(A ∪ B) | 两表 | NULL填充 | 否 | 需要完整并集(用 UNION 模拟) |
5. 面试/笔试高频考点总结
- INNER JOIN vs WHERE 写法区别(性能、语义)
- LEFT JOIN + WHERE 条件放错位置导致结果变内连接
- 如何模拟 FULL JOIN(UNION + WHERE IS NULL 去重)
- 多表 JOIN 时顺序影响(尤其是 LEFT + INNER 混用)
- ON vs USING(USING 只适用于同名字段)
- 自连接(SELF JOIN)、交叉连接(CROSS JOIN)也常一起问
一句话口诀:
内连接求交集,外连接求并集(LEFT 保左、RIGHT 保右、FULL 保双),没匹配的就 NULL 补位。
你现在是想看具体业务场景示例(比如订单+用户、员工+部门),还是想练几道 JOIN 题型?
或者有某个 JOIN 的 WHERE/ON 细节困惑?告诉我,继续深挖!