1. 语义区别(LEFT JOIN 时差异最明显)
-- 方式1: 条件在 ON 中
SELECT * FROM 订单表 m
LEFT JOIN 客户表 p ON p.ID = m.CUSTOMER_ID AND p.STATUS = 1
-- 方式2: 条件在 WHERE 中
SELECT * FROM 订单表 m
LEFT JOIN 客户表 p ON p.ID = m.CUSTOMER_ID
WHERE p.STATUS = 1
| 方式 |
结果 |
| ON 中 |
保留所有订单,不满足 STATUS=1 的客户字段显示 NULL |
| WHERE 中 |
过滤掉不满足条件的行,LEFT JOIN 退化成 INNER JOIN |
2. 执行顺序
FROM → ON → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
- ON:在 JOIN 生成临时表时 过滤
- WHERE:在临时表 生成后 过滤
3. 性能区别
INNER JOIN 时:几乎无差别
-- 这两个等价,优化器会生成相同的执行计划
SELECT * FROM m INNER JOIN p ON p.ID = m.PRD_ID AND p.SEX = 'M'
SELECT * FROM m INNER JOIN p ON p.ID = m.PRD_ID WHERE p.SEX = 'M'
MySQL 优化器会自动将 WHERE 条件下推到 ON 中。
LEFT JOIN 时:ON 更优
-- ✅ 推荐:条件在 ON 中,减少中间结果集
SELECT * FROM m
LEFT JOIN p ON p.ID = m.PRD_ID AND p.DELETE_FLAG = 0
-- ❌ 不推荐:先生成大临时表,再过滤
SELECT * FROM m
LEFT JOIN p ON p.ID = m.PRD_ID
WHERE p.DELETE_FLAG = 0 OR p.ID IS NULL -- 还要处理 NULL 情况
| 对比项 |
ON 条件 |
WHERE 条件 |
| 临时表大小 |
更小(提前过滤) |
更大(全量 join 后过滤) |
| 索引利用 |
可用于 join 优化 |
join 后再扫描 |
| NULL 处理 |
自动保留主表行 |
需要额外处理 |
4. 实际建议
-- ✅ 正确写法:关联条件 + 过滤条件都放 ON
LEFT JOIN tb_product_dev_info p
ON p.ID = m.PRD_ID
AND p.DELETE_FLAG = 0
AND p.IS_OUT = 0
-- ❌ 避免:WHERE 中过滤 LEFT JOIN 的表
LEFT JOIN tb_product_dev_info p ON p.ID = m.PRD_ID
WHERE p.DELETE_FLAG = 0 -- 会把 LEFT JOIN 变成 INNER JOIN
5. 总结
| 场景 |
推荐 |
原因 |
| INNER JOIN |
都行 |
优化器会自动优化 |
| LEFT JOIN 过滤右表 |
ON |
保持 LEFT JOIN 语义,性能更好 |
| LEFT JOIN 过滤左表 |
WHERE |
ON 中过滤左表无效 |