LEFT JOIN 的右表过滤条件必须写在 ON 中,否则会丢失左表无匹配的行;WHERE 仅适用于左表字段过滤或对连接结果的整体判断,多表连接时 ON 条件只能引用当前及左侧已出现的表。

LEFT JOIN 的右表条件必须写在 ON 里,否则左表会丢数据
想保留左表所有行?那所有针对右表的过滤条件——比如 o.status = 'paid'、o.created_at > '2023-01-01'——都得塞进 ON 子句。一旦错扔到 WHERE,MySQL 就会把右表为 NULL 的整行干掉,LEFT JOIN 彻底失效,变成事实上的 INNER JOIN。
哪些条件可以放心放 WHERE
WHERE 不是不能用,而是只能放两类条件:
- 纯左表字段的过滤,比如
u.active = 1或u.name LIKE 'A%'—— 这类不影响左表完整性 - 对连接结果的全局判断,比如
o.id IS NULL(查无订单用户)或COALESCE(o.status, 'no_order') = 'no_order'
只要 WHERE 里出现右表字段的非空约束(如 o.amount > 0),就等于主动剔除左表孤立行。
多表 LEFT JOIN 时 ON 条件只能引用紧邻的表
写 A LEFT JOIN B ON ... LEFT JOIN C ON ... 时:
- B 的
ON只能用 A 和 B 的字段,不能提前引用 C - C 的
ON可以用 A、B、C 字段,但若逻辑上依赖 B 的过滤结果(比如“只连 B 中 status='paid' 的记录对应的 C”),就不能靠ON直接写,得先用子查询或 CTE 把 B 拎出来 - 别指望
ON u.id = o.user_id AND u.role = 'admin'能在右表连接中起作用——它语法合法,但语义模糊,易读性差,也容易被优化器误判
性能敏感场景:优先在子查询里过滤右表
当右表数据量大、且过滤条件能大幅减少中间集时,显式子查询往往比堆在 ON 里更可控:
SELECT u.name, o.amount FROM users u LEFT JOIN ( SELECT user_id, amount FROM orders WHERE status = 'paid' AND created_at > '2025-01-01' ) o ON u.id = o.user_id;
相比 ON u.id = o.user_id AND o.status = 'paid' AND o.created_at > '2025-01-01',子查询让过滤提前发生,避免大表全扫描后再关联。不过要注意:子查询无法引用左表字段(除非是相关子查询,但那又引入新复杂度)。
最常被忽略的点:ON 和 WHERE 的执行时机不是“谁先谁后”的问题,而是“作用域不同”。ON 是连接规则,WHERE 是结果筛子——用错位置,不是慢一点,是数据直接错。


















