LEFT JOIN在WHERE中对右表字段加条件会使其退化为INNER JOIN,因NULL值被过滤;正确做法是将右表条件移至ON子句中,确保左表行保留。

WHERE里写右表字段,LEFT JOIN就失效了
LEFT JOIN 本意是保留左表所有行,右表没匹配上就填 NULL。但一旦在 WHERE 子句里对右表字段加条件(比如 t2.status = 'active'),数据库会先完成连接,再执行过滤——此时右表字段为 NULL 的整行,因 NULL = 'active' 判定为 FALSE(不是 TRUE),直接被踢出结果集。左表数据也就跟着消失,LEFT JOIN 实际退化成 INNER JOIN。
ON 和 WHERE 的执行阶段完全不同
SQL 执行顺序固定:FROM → ON → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。ON 是连接时的“准入规则”:决定哪些右表行能跟当前左表行配对;不满足的右表行跳过,但左表行照常保留(右侧填 NULL)。WHERE 是连接后的“筛子”:作用于已拼好的临时结果集,整行不满足就扔,不管左表多无辜。
- 错误写法:
LEFT JOIN t2 ON t1.id = t2.t1_id WHERE t2.status = 'active'→ 右表没匹配或状态不符的左表行全丢 - 正确写法:
LEFT JOIN t2 ON t1.id = t2.t1_id AND t2.status = 'active'→ 状态不符的右表行不参与连接,但左表行仍在 - 注意:
ON中不能用COALESCE(t2.status, 'inactive') = 'active',因为ON阶段对NULL的处理不支持这种兜底逻辑
多层 LEFT JOIN 时右表条件放错位置更危险
比如查用户 + 订单 + 订单明细,想只取电子类商品订单:
- 错误写法:
LEFT JOIN order_items oi ON o.id = oi.order_id WHERE oi.category = 'electronics'→ 没买电子产品的用户全消失 - 正确写法:
LEFT JOIN order_items oi ON o.id = oi.order_id AND oi.category = 'electronics'→ 用户还在,没电子类订单的oi字段为NULL - 如果第二层
ON依赖第一层右表字段(如o.product_id = t3.id),而o.product_id本身是NULL,那第三层连接必然失败,得改用左表字段直接关联(如t1.preferred_product_id = t3.id)
调试时最容易被忽略的 NULL 陷阱
看到结果里右表字段批量为 NULL,别急着怀疑数据或索引,先检查两件事:
- 是否在
WHERE里写了右表字段的等值判断?哪怕只是t2.id IS NOT NULL,也会过滤掉所有没匹配的行 - 连接字段本身是否含
NULL?比如t2.ref_id IS NULL,那么ON t1.id = t2.ref_id永远不成立(NULL = anything恒为UNKNOWN),右表字段必然全NULL -
IN不能兜住NULL:写t2.status IN ('active', NULL)没用,NULL IN ()永远返回UNKNOWN,等价于FALSE

















