on决定连接匹配规则,where执行连接后的行级筛选;left join中右表条件须写在on中才能保留左表数据,否则where中的右表非空判断会剔除null行导致等效inner join。

ON 是连接时的匹配规则,WHERE 是连接后的行级筛选
MySQL 执行器先做 JOIN,再做 WHERE。这个顺序不可逆,且直接影响结果集结构。对 LEFT JOIN 来说,ON 决定“哪些右表行能和左表某行配对”,而 WHERE 是在配对完成(含补 NULL)之后,把整行拿去判断“要不要留下”。一旦 WHERE 中出现右表字段的非空判断(比如 WHERE b.status = 'active'),所有右表为 NULL 的行就会被直接剔除——左表数据就丢了。
LEFT JOIN 中右表条件写在 ON 里,不会丢左表数据
这是最常踩坑的地方。例如查“所有用户及其活跃订单”,如果把活跃条件放 WHERE,结果只返回有活跃订单的用户;放 ON 才能保留无活跃订单的用户(右表字段为 NULL)。
-
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'active':左表用户全保留,右表只取 status='active' 的订单,不满足的填NULL -
LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 'active':先全量连,再筛,o.status为NULL的行被过滤掉,等效于INNER JOIN - 若右表条件涉及索引字段(如
o.created_at > '2025-01-01'),放ON可让优化器提前剪枝,减少连接数据量
INNER JOIN 里 ON 和 WHERE 大部分情况等价,但语义和可维护性差很多
逻辑上结果一致,但执行阶段不同:ON 在连接过程中过滤,WHERE 在连接后过滤。虽然优化器通常能重写,但混用会模糊意图。
- 推荐统一把关联逻辑(包括业务过滤)写进
ON,比如INNER JOIN scores s ON s.student_id = u.id AND s.score >= 60 - 若把
s.score >= 60放WHERE,后续维护者可能误以为这是“连接后才筛”,尤其嵌套多层时容易误解 - 旧版 MySQL 或复杂执行计划下,
WHERE中的关联条件可能无法触发index condition pushdown,导致性能下降
ON 后可以写非关联条件,但仅对外连接有意义
ON 子句里允许出现不涉及连接字段的条件(比如 AND b.type = 'vip'),但这对 INNER JOIN 和 CROSS JOIN 没实际作用,反而干扰可读性。
- 在
LEFT JOIN中,ON a.id = b.user_id AND b.type = 'vip'表示:“只尝试用 vip 类型的记录去连,其他类型不参与匹配,但左表仍保留” - 如果写成
ON a.id = b.user_id WHERE b.type = 'vip',所有b.type不是 vip 或为NULL的行都会消失 - 混合非等值、非关联条件会让执行计划更难优化,某些场景下数据库可能放弃使用右表索引
IS NOT NULL 或 = 判断,放在不同位置,结果就可能从“全量左表”变成“仅有交集”。











