LEFT JOIN结果数据变少是因为WHERE中对右表字段的条件(如status = 'paid')会过滤掉右表为NULL的整行,使LEFT JOIN退化为INNER JOIN;正确做法是将右表筛选条件移至ON子句。

LEFT JOIN 中右表字段加条件,为什么结果会少数据?
因为 WHERE 会对连接后的整行做最终裁剪,而右表无匹配时字段值是 NULL。SQL 规定任何与 NULL 的等值或不等值比较(如 status = 'paid'、num != 0)结果都是 FALSE,整行就被干掉了——左表那条记录也就没了。
这不是数据库“bug”,是标准行为。你写 WHERE b.status = 'paid',等于在说:“只要右表 status 不是 'paid'(含 NULL),这行就不要”。LEFT JOIN 的“保留左表全部”特性,在 WHERE 阶段已失效。
ON 里写右表条件,真的只过滤右表吗?
是的,但要注意:它只影响“哪些右表行能参与连接”,不影响左表是否出现。比如 LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid':
- 所有用户(
u)都会出现在结果中 - 只有
status = 'paid'的订单才会被连上来;其他订单(如'cancelled'或NULL)直接不参与匹配 - 没支付订单的用户,
o.*全为NULL,但用户行仍在
什么时候必须把条件放 ON,不能放 WHERE?
只要你的业务语义是“查所有 A,附带符合条件的 B”,就必须放 ON。典型场景包括:
- 统计每个用户的已支付订单数:
COUNT(o.id),而非COUNT(*) - 展示用户列表 + 最近一笔有效订单时间,空用户也要显示
- 导出全量主表数据,仅关联启用状态的配置项
反例:写成 WHERE o.status = 'paid',结果里压根没有无订单用户,也漏掉 status 为 NULL 或 'draft' 的订单关联行。
多表 LEFT JOIN 时,ON 条件容易串表吗?
不会“串”,但容易误以为能跨层引用。比如:
SELECT * FROM a LEFT JOIN b ON a.id = b.aid LEFT JOIN c ON b.id = c.bid AND c.flag = 1
这里 c.flag = 1 只约束 c 表自身,和 a 无关;但如果写成 WHERE c.flag = 1,就会把 b 有数据但 c 不满足 flag = 1 的整行(含 a 和 b)都剔除。
最易被忽略的是:ON 阶段右表尚未被 WHERE 筛过,所以 ON 里对右表字段的判断(尤其是涉及索引字段)直接影响连接性能——条件写得宽泛,中间结果集就膨胀。










