LEFT JOIN后WHERE中对右表字段加非NULL条件会丢失左表孤立行,等效于INNER JOIN;正确做法是将筛选条件移至ON子句以保留左表全部记录。

LEFT JOIN后WHERE里写右表字段会变成隐式INNER JOIN
这是最常见的误解来源:你以为保留了左表全部行,但WHERE子句在JOIN完成后再过滤,一旦对右表字段(比如orders.amount)加非NULL条件,就会把右表为NULL的那些左表行直接筛掉。
例如:SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.amount > 100——这条语句实际效果等价于INNER JOIN,因为o.amount > 100对NULL不成立(SQL中NULL > 100结果是UNKNOWN,被WHERE当作假值丢弃)。
想保留左表所有行,条件必须写进ON子句
真正想表达“查所有用户,只关联金额大于100的订单”,就得把筛选逻辑下推到JOIN条件里:
SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.amount > 100
这样,o.amount > 100参与连接匹配,而不是事后过滤。没匹配上的用户依然保留,o.amount为NULL。
-
ON里的条件决定“哪些右表行能连上来” -
WHERE里的条件决定“连完之后还留下哪些整行” - 右表字段在
WHERE中出现且含非NULL约束(如IS NOT NULL、> 0、!= 'x'),就必然丢失左表孤立行
常见踩坑场景和对应写法
以下错误写法都会意外丢数据:
- 查“所有学生及他们的及格课程成绩”,却写成:
WHERE score >= 60→ 错,应改用ON s.id = sc.student_id AND sc.score >= 60 - 查“所有部门及在职员工数”,却用
WHERE emp.status = 'active'→ 错,会导致无在职员工的部门消失;正确是ON d.id = e.dept_id AND e.status = 'active',再COUNT(e.id) - 判断右表是否存在满足条件的记录,误用
WHERE o.created_at > '2025-01-01'→ 应改用ON ... AND o.created_at > '2025-01-01',否则NULL日期行全被剔除
性能与可读性提醒
把条件从WHERE挪到ON不只是语义问题,也影响执行计划:
- 数据库可能提前过滤右表数据,减少JOIN中间结果集大小
- 但若
ON条件太复杂(比如含函数或子查询),某些引擎无法下推索引,反而变慢 - 阅读时务必注意:右表字段出现在
WHERE,几乎一定意味着你放弃了LEFT语义
最稳妥的习惯是——只要用了LEFT JOIN,就先检查WHERE里有没有右表列;有,就问自己一句:这行没匹配时,该不该保留?该,就挪进ON。

















