五种 JOIN
| 类型 | 说明 |
|---|---|
INNER JOIN | 只保留两边都匹配的行 |
LEFT JOIN | 保留左表全部行,右表没匹配到的补 NULL |
RIGHT JOIN | 保留右表全部行,左表没匹配到的补 NULL |
FULL OUTER JOIN | 两边都保留,没匹配到的补 NULL |
CROSS JOIN | 笛卡尔积,左表每行和右表每行两两组合 |
MySQL 不支持 FULL OUTER JOIN,可以用 LEFT JOIN UNION RIGHT JOIN 模拟。SQLite 也不支持 RIGHT JOIN 和 FULL OUTER JOIN。
LEFT JOIN 经典套路
面试里 LEFT JOIN 考得最多,经典用法是"找出没有 XXX 的记录":
-- 找出从未下过单的用户
SELECT u.user_id, u.name
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.user_id IS NULL; 原理:LEFT JOIN 保留左表所有行,没有匹配的订单时 o.user_id 为 NULL。WHERE o.user_id IS NULL 筛出这些"没匹配到"的行。
等价写法:
-- NOT EXISTS
SELECT user_id, name FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id);
-- NOT IN(注意:如果子查询有 NULL 值,NOT IN 的行为可能不符合预期)
SELECT user_id, name FROM users
WHERE user_id NOT IN (SELECT user_id FROM orders); 多表 JOIN
实际业务中经常需要 JOIN 三四张表。写的时候注意:
- 先画清楚表关系(哪张表和哪张表通过什么字段关联)
- 从最核心的表开始写,逐步 JOIN
- 注意 JOIN 顺序对 LEFT JOIN 结果的影响
练习
练习
没有成绩的学生
给定
students 和 scores 两张表,找出没有任何成绩记录的学生姓名。点击「运行」查看查询结果
查看答案
参考答案
SELECT s.name
FROM students s
LEFT JOIN scores sc ON s.id = sc.student_id
WHERE sc.student_id IS NULL; 解析
LEFT JOIN 保留所有学生,没有成绩记录的学生在 scores 侧全为 NULL。Dave 没有任何成绩,会被筛出来。
练习
没有员工的部门
给定
departments 和 employees 两张表,找出没有任何员工的部门名称。点击「运行」查看查询结果
查看答案
参考答案
SELECT d.dept_name
FROM departments d
LEFT JOIN employees e ON d.dept_id = e.dept_id
WHERE e.emp_id IS NULL; 解析
同样的 LEFT JOIN + IS NULL 模式。Finance 部门没有员工,会被筛出来。