INNER JOIN、LEFT JOIN、RIGHT JOIN 和 FULL JOIN 有什么区别?
简化版
INNER JOIN 只保留两边匹配的行;LEFT JOIN 保留左表全部行;RIGHT JOIN 保留右表全部行;FULL OUTER JOIN 保留两边全部行,未匹配一侧补 NULL。MySQL 目前没有直接支持 FULL OUTER JOIN,需要用其他写法模拟。
详细版
假设 department 是部门表,employee 是员工表,两者用 department_id 关联:
| 连接类型 | 返回结果 |
|---|---|
INNER JOIN | 只返回有对应部门的员工与有员工匹配的部门 |
LEFT JOIN | 返回左表全部行,右表无匹配时补 NULL |
RIGHT JOIN | 返回右表全部行,左表无匹配时补 NULL |
FULL OUTER JOIN | 返回两表所有行,任一侧无匹配都补 NULL |
CROSS JOIN | 返回笛卡尔积,即两表行数相乘 |
连接条件应优先写在 ON 中。对外连接而言,把右表条件写进 WHERE 可能过滤掉补出的 NULL 行,使结果效果接近内连接。
完整版教学
一、INNER JOIN 只要交集
SELECT e.name, d.name AS department_name
FROM employee AS e
INNER JOIN department AS d
ON d.id = e.department_id;
只有 e.department_id = d.id 成立的组合才会进入结果。一个部门有多名员工时,部门行会与每名员工分别配对,因此连接结果不保证与任一原表行数相同。
二、LEFT JOIN 保住左表
SELECT d.name, e.name AS employee_name
FROM department AS d
LEFT JOIN employee AS e
ON e.department_id = d.id;
即使某个部门没有员工,它也会出现一次,此时员工列为 NULL。查“没有员工的部门”可以在连接后判断右表主键:
SELECT d.*
FROM department AS d
LEFT JOIN employee AS e ON e.department_id = d.id
WHERE e.id IS NULL;
应判断右表中确定非空的列,主键最合适;若判断本身允许为 NULL 的业务列,会误判。
三、RIGHT JOIN 与 FULL OUTER JOIN
RIGHT JOIN 与交换左右表后的 LEFT JOIN 等价。团队通常统一使用 LEFT JOIN,可降低阅读时切换主表视角的成本。
FULL OUTER JOIN 同时保住两边。PostgreSQL、Oracle 等支持该语法;MySQL 不直接支持。MySQL 中可按业务需求用“左连接结果 + 仅右侧未匹配行”的 UNION ALL 模拟,不能简单把两个完整外连接结果拼接,否则匹配行会重复。
四、ON 条件和 WHERE 条件的陷阱
下面两条对 LEFT JOIN 的含义不同:
-- 保留所有部门,只连接在职员工
LEFT JOIN employee AS e
ON e.department_id = d.id AND e.status = 'active'
-- 连接完成后只保留在职员工,空部门也被过滤
LEFT JOIN employee AS e ON e.department_id = d.id
WHERE e.status = 'active'
第二种写法会拒绝 e.status 为 NULL 的补齐行,通常等效于内连接效果。是否应该这样写取决于业务语义,而不是固定规则。
五、连接为什么会产生重复行
SQL 连接按匹配组合输出。一对多连接会让“一”侧重复,多对多连接可能迅速放大行数。发现重复时应先检查关系基数和连接条件是否完整,而不是立即加 DISTINCT 掩盖问题。
连接列通常需要合适索引,但最终访问方式仍由数据量、选择性和优化器成本决定。索引也不能修复漏写连接条件造成的笛卡尔积。
六、常见误区与追问
| 连接类型 | 保留规则 | 无匹配时 |
|---|---|---|
INNER JOIN | 只保留匹配行 | 两侧不匹配都丢弃 |
LEFT JOIN | 保留左表行 | 右表列补 NULL |
RIGHT JOIN | 保留右表行 | 左表列补 NULL |
FULL OUTER JOIN | 两侧都保留 | 不匹配侧补 NULL |
- 误区:LEFT JOIN 后在 WHERE 写右表条件仍然保留左表全部行。
WHERE right_col = ...会把补出来的NULL行过滤掉,效果可能退化为内连接。 - 误区:JOIN 只会按两表主键一对一合并。 连接按匹配关系组合行,一对多、多对多都会放大行数,重复通常来自数据关系而不是数据库出错。
- 误区:RIGHT JOIN 比 LEFT JOIN 有特殊能力。 二者只是保留侧不同,多数场景可通过交换表顺序改写成
LEFT JOIN,可读性更稳定。 - 追问:FULL OUTER JOIN 适合什么场景? 需要同时保留两侧不匹配数据时使用,例如对账时找左有右无、右有左无以及双方匹配的记录。
- 追问:ON 和 WHERE 的核心区别是什么?
ON决定两表如何匹配并影响外连接补空,WHERE是连接结果形成后的过滤。 - 追问:怎样避免连接后聚合被放大? 先确认基数关系,必要时先在子查询中把多方聚合到一行,再与主表连接。
记忆钩子:内连接看匹配,外连接看保留侧;外连接最怕把右表过滤条件错放到
WHERE。
七、加强记忆
INNER 取匹配交集,LEFT 保左,RIGHT 保右,FULL 保两边,CROSS 做组合。外连接最容易错在过滤位置:ON 决定右侧哪些行参与匹配,WHERE 对连接后的整份结果再次过滤,可能把补出的 NULL 行一起删掉。