外连接是什么
LEFT JOIN 与 RIGHT JOIN 属于外连接:保留一侧的全部行,另一侧没有匹配时补 NULL。左连接保留左表全集,右连接保留右表全集,工程上一般统一改写成左连接,保证阅读方向一致。它最容易用错的地方是过滤条件的位置——条件写在 ON 里表示匹配时的附加限制,写在 WHERE 里表示对结果集过滤,对右表列加非空条件会把左连接悄悄退化成内连接。
基本信息
| 用途 | 保留单侧全集,另一侧可为空 |
|---|---|
| 语法 | A LEFT JOIN B ON 连接条件 |
| 方向 | LEFT 保留左表全部行,RIGHT 保留右表全部行 |
| NULL 补位 | 无匹配时另一侧所有列为 NULL |
| 条件位置 | ON 影响匹配过程,WHERE 影响最终结果 |
| 常见替代 | NOT EXISTS 表达寻找缺失关联,语义比 NOT IN 更安全 |
典型使用场景
- 查哪些用户一单都没有:用户表左连接订单表,右表主键为空
- 统计每用户订单数并包含零单用户:对右表列计数而不是计数全部行
- 主记录补可选的附属信息(备注、标签),缺失时不能丢掉主记录
- 数据质量核查:左连接找出明细表里没有主表记录的孤儿行
- 报表需要完整维度:日期维表左连接事实表,无数据的日期补零
- 迁移前后比对:用外连接找出只在一侧存在的记录
左连接找无订单用户
左连接找无订单用户
SELECT u.id, u.name # 只取左表列,避免右表整列空值干扰阅读
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.id IS NULL; # 右表主键为空即表示没有匹配上统计每用户订单数含零单
统计每用户订单数含零单
SELECT u.id, COUNT(o.id) AS cnt # 计右表列,无匹配时结果为 0
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id AND o.status = 'paid' # 过滤写在 ON 里,零单用户才不会被剔除
GROUP BY u.id
ORDER BY cnt DESC; # 按单量倒序看活跃度ON 与 WHERE 的语义差别
| 写法 | 结果 | 适用 | 说明 |
|---|---|---|---|
| ON o.status='paid' | 右表只保留已支付行,左表行全部保留 | 统计含零单用户 | 限制匹配范围的标准位置 |
| WHERE o.status='paid' | 未匹配与未支付的行都被过滤 | 多半是写错了 | 等价于内连接,左表空值行全部消失 |
| WHERE o.id IS NULL | 只保留没有匹配的左表行 | 找缺失关联 | 最常用的缺口排查写法 |
| ON 里写左表条件 | 不减少左表行,只影响是否匹配 | 不推荐 | 左表条件放 WHERE 更直观 |
| COUNT(*) | 无匹配行也被算成一条 | 极易出错 | 统计数量会整体虚高 |
| COUNT(o.id) | 无匹配计为 0 | 统计含零单 | 外连接统计的正确写法 |
左连接后在 WHERE 里对右表列加非空条件会把外连接退化成内连接,零单用户凭空消失;需要限制右表匹配范围时,把条件写在 ON 里。
计数全部行与计数右表列在外连接下结果不同:前者把补 NULL 的行也算进去,后者跳过空值得到 0,统计含零单用户时只能用后者。
右连接与左连接语义对称,但多表混用时阅读成本高,工程上通常重排表的顺序改成左连接,让整条语句的连接方向保持一致。
外连接检查清单
- 先确认要保留哪一侧的全部行,表的书写顺序与连接方向必须对应
- 右表的匹配限制写 ON,结果集过滤写 WHERE,两者不能混用
- 统计总量与统计含零值用的是不同写法,先明确业务要哪个口径
- 找缺失关联用右表主键判空,比判断其他业务列更可靠
- 外连接链条里夹了内连接会截断结果,每段都要验证行数
- 补空值的结果交给应用前先定义空值语义,避免被当成零值
外连接把右表条件写错位置时不会报错,结果也不为空,只是少了一批行;这类错误很难靠肉眼发现,建议用同样的条件分别做统计计数再比对。