LEFT JOIN 后WHERE中对右表字段加非空条件会退化为INNER JOIN,因执行顺序是先JOIN后WHERE;正确做法是将右表筛选条件移至ON子句,并检查索引与执行计划。

LEFT JOIN 后 WHERE 里写了右表字段的非空条件,就会筛掉 NULL 行,结果自然只剩匹配项——不是数据库“变聪明了”,是执行顺序决定的。
WHERE 中出现右表字段且非 IS NULL 就会退化
SQL 执行顺序是:先完成 LEFT JOIN(生成含 NULL 的中间结果),再执行 WHERE 过滤。只要 WHERE 里有类似 o.status = 'paid'、d.dim_month > '2025-07' 这类对右表字段的非空判断,所有右表字段为 NULL 的行(即左表没连上的那些)全被干掉。
-
WHERE o.status = 'paid'→ 退化,只留已支付订单的用户 -
WHERE o.status IS NULL→ 不退化,反而专挑没订单的用户 -
WHERE u.created_at > '2025-01-01'→ 安全,只过滤左表 -
WHERE o.status = 'paid' OR o.status IS NULL→ 逻辑绕、易漏、不推荐
正确做法:右表筛选条件必须写进 ON 子句
想保留左表全部记录,同时只关联满足业务条件的右表数据,就得把右表的筛选逻辑收进对应 LEFT JOIN 的 ON 后面,用 AND 连接。
- 错误写法:
LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid' - 正确写法:
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid' - 多层
LEFT JOIN时,每层右表的条件都要各自收进自己的ON,比如:LEFT JOIN order_items oi ON o.id = oi.order_id AND oi.category = 'electronics' -
ON后不能用OR(语义混乱,多数引擎不支持)
动态 SQL 和 ORM 场景下最容易踩坑
MyBatis、JDBC 拼条件时,前端传了个 dept_name,后端直接塞进 WHERE,LEFT JOIN 就悄悄变成 INNER JOIN,但开发可能完全没意识到关联语义已变。
- 更隐蔽的是性能问题:条件从
WHERE挪到ON后,如果右表没建(cal_dt, dim_month)这类复合索引,谓词下推可能失效,查询反而变慢 - 改写前必须看
EXPLAIN,不能只盯语法 - Oracle 兼容模式(如金仓 KES)下,
(+)写法漏掉任何一个右表字段后的(+),也会退化
真正容易被忽略的点是:退化不是偶然,而是执行模型的必然;而修复它不只是改个位置,还得同步检查索引和执行计划——否则修好了语义,拖垮了性能。

















