ON 在 JOIN 过程中决定匹配行,WHERE 在 JOIN 后筛选结果;LEFT JOIN 中右表条件放 ON 保留左表行、放 WHERE 则退化为 INNER JOIN;INNER JOIN 下二者通常等效但不可依赖;务必用 EXPLAIN 验证执行计划。

WHERE 和 ON 的执行时机完全不同
JOIN 之后再 WHERE 过滤,和把条件写进 ON 里,看起来结果一样,实际执行计划、数据量、索引使用都可能差很远。核心区别在于:ON 是在 JOIN 过程中决定“哪些行能匹配上”,而 WHERE 是在 JOIN 完成后对整张结果表做筛选。
LEFT JOIN 中 ON 后加条件 ≠ WHERE 后加条件
这是最容易出错的场景。如果在 LEFT JOIN …… ON 里加右表的过滤条件(比如 ON b.status = 'active'),没匹配上的左表行仍会保留,右表字段为 NULL;但若写成 LEFT JOIN …… ON …… WHERE b.status = 'active',就会把所有右表不满足的行(包括 NULL 行)全干掉,LEFT JOIN 变相退化成 INNER JOIN。
-
LEFT JOIN b ON a.id = b.a_id AND b.status = 'active'→ 保留所有 a 行,b 不满足时 b.* 全为 NULL -
LEFT JOIN b ON a.id = b.a_id WHERE b.status = 'active'→ 实际只留 a 和 b 都有且 b.status 匹配的行 - 用 EXPLAIN 看执行计划,后者常出现
Using where且 rows 显著变少,说明过滤发生在 JOIN 后,已丢失左表冗余信息
INNER JOIN 下 ON 和 WHERE 效果通常一致,但别依赖优化器
理论上,INNER JOIN 中 ON 和 WHERE 的等价条件会被优化器合并处理,执行计划往往一样。但实际中不能假设一定如此:
- 多个 JOIN 时,条件位置会影响驱动表选择 —— 把过滤强的条件尽量放在对应表的
ON子句里,有助于优化器提前剪枝 - 含函数或类型转换的条件(如
WHERE UPPER(b.name) = 'ABC')放WHERE更安全,放ON可能干扰索引下推 - 某些旧版 MySQL(5.6 以前)或复杂嵌套查询中,
WHERE条件延迟应用会导致临时表膨胀
用 EXPLAIN 验证,别猜执行逻辑
有没有真正走索引、是否用了临时表、rows 扫描量多少——这些全得靠 EXPLAIN 看,而不是看 SQL 写得“顺不顺眼”。尤其当表数据量上百万后,ON 和 WHERE 差一个条件,执行时间可能从 0.02s 涨到 12s。
- 重点关注
type(最好为ref或range)、key(是否命中索引)、rows(预估扫描行数) - 对比两个版本的
EXPLAIN输出,哪怕只是把WHERE b.x = 1挪到ON里,rows可能从 500000 降到 300 - 注意
Extra字段:出现Using join buffer或Using temporary就是危险信号
真实业务里,JOIN 条件的位置不是语法风格问题,是数据流控制问题。ON 是建表关系时就该想清楚的约束,WHERE 是最终呈现前的裁剪。两者混用,等于一边搭桥一边拆砖。