LEFT JOIN中右表条件必须写在ON里,否则WHERE中引用右表字段会过滤掉左表无匹配的行,使结果等效于INNER JOIN;INNER JOIN中ON和WHERE虽结果相同但执行路径不同,ON可提前剪枝优化性能。

LEFT JOIN里右表条件必须写在ON里
LEFT JOIN 的语义是“保留左表所有行”,但只要你在 WHERE 里引用右表字段(比如 o.status = 'paid'),数据库就会把右表为 NULL 的行全部剔掉——结果等价于 INNER JOIN,左表数据直接丢失。
常见错误现象:查用户列表 + 订单信息,本想显示所有用户、只关联已支付订单,结果只看到有支付记录的用户。
- ✅ 正确写法:
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid'→ 左表用户全在,没匹配到的o.*为NULL - ❌ 错误写法:
LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid'→ 没支付订单的用户被过滤掉 - ⚠️ 即使是
WHERE o.id IS NOT NULL,也破坏 LEFT JOIN 语义,等效于 INNER JOIN
INNER JOIN中ON和WHERE逻辑结果相同但执行路径不同
表面上看,INNER JOIN 里把条件放 ON 还是 WHERE 都出一样结果,但底层执行差异明显:优化器对 ON 条件能更早剪枝,影响连接策略选择和索引使用。
性能影响举例:假设 orders 表有百万行,users 表十万行,连接条件含 user_id 索引:
-
ON u.id = o.user_id AND o.created_at > '2025-01-01'→ 优化器可能先用索引扫出近期订单,再 join,扫描量小 -
ON u.id = o.user_id WHERE o.created_at > '2025-01-01'→ 先全量 join,再 filter,中间临时表更大,估算偏差风险高
多表JOIN时ON条件位置决定是否提前排除无效行
三张表连查时,把过滤条件放在靠前的 ON 子句里,能避免无谓连接后续表的无效数据。
比如:A INNER JOIN B ON A.id = B.a_id INNER JOIN C ON B.id = C.b_id AND C.type = 'active'
-
C.type = 'active'放在第二个ON里 → 只对已匹配的B行去查C,且只查type = 'active'的记录 - 如果挪到
WHERE→ 先完成A-B-C全量连接,再过滤,C 表中大量type != 'active'的行参与了连接计算 - 尤其当
C是大宽表或关联成本高时,性能差距显著
WHERE可以跨表筛选但ON只能约束当前两表
ON 只能引用当前 JOIN 的两张表字段,而 WHERE 可以自由引用任意已出现在 FROM 中的表字段,甚至聚合或子查询(只要语法允许)。
典型混合用法:
- 外连接用
ON控制关联逻辑(如LEFT JOIN orders o ON u.id = o.user_id AND o.is_deleted = 0) - 再用
WHERE筛主表状态(如WHERE u.is_active = 1)→ 安全,不丢行,因为不依赖右表非空 - 但
WHERE u.id IN (SELECT user_id FROM logs WHERE action = 'login')这类跨表逻辑只能放WHERE,不能塞进ON
最容易被忽略的是:写完 JOIN 后下意识把所有条件往 WHERE 里堆,却没问一句——这个条件,是在定义“怎么连”,还是在定义“要哪些”。答案不对,结果就偏了。

















