left join在where中对右表字段加条件会使其等效于inner join,因null值被过滤;正确做法是将右表筛选条件写入on子句,如on u.id = o.user_id and o.status = 'paid'。

WHERE里写了右表字段,LEFT JOIN就失效了
LEFT JOIN本身不会丢左表数据,但一旦在WHERE子句中对右表字段加条件(比如WHERE o.status = 'paid'),数据库会先完成连接,再执行过滤——而右表没匹配上的行,其字段全是NULL,NULL = 'paid'判定为FALSE,整行被剔除,结果等效于INNER JOIN。
常见错误现象包括:
- 明明写了
LEFT JOIN,返回行数却和INNER JOIN一样 - 左表某些ID完全不出现在结果里
-
EXPLAIN显示用了索引,但实际查不到预期数据
根本原因是SQL执行顺序:ON决定“怎么连”,WHERE决定“连完之后留哪些行”。右表筛选必须塞进ON,不能放WHERE。
ON子句里漏掉右表筛选条件,导致逻辑错位
想保留左表全部、又只关联右表中符合条件的记录,唯一可靠方式是把右表条件写进ON,用AND连接。例如:
SELECT u.id, u.name, o.order_id FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid';
这样没支付订单的用户仍保留,o.order_id为NULL;而写成WHERE o.status = 'paid',这些用户就彻底消失。
注意几个关键点:
- 不能用
COALESCE(o.status, 'inactive') = 'paid'替代——ON中COALESCE对NULL无效,必须显式写o.status = 'paid' - 如果真要查“没关联记录”的左表行,可以用
WHERE o.id IS NULL,这个可以放心放WHERE - 多层
LEFT JOIN时,第二层别依赖第一层右表字段(如t2.product_id),否则t2.product_id为NULL会导致下层连接失败
GROUP BY字段没包含右表列,空缺被压缩成一行
LEFT JOIN + GROUP BY组合下,如果GROUP BY只用左表字段(比如GROUP BY month),所有右表为NULL的行会被压进同一个分组,看起来像“丢了”某些月份或分类。
真正要看出空缺,得让右表字段参与分组或判空:
- 用
COUNT(o.id)而非COUNT(*):前者只统计非NULL订单,后者统计所有左表行 - 构造完整左表维度(如用
UNION生成12个月份),再LEFT JOIN业务表 -
GROUP BY字段必须和SELECT中非聚合列严格一致,包括表别名和原始列名
误把LEFT JOIN当INNER JOIN用,却没意识到语义冲突
业务主语决定了该用哪种JOIN:想“以哪张表为基准呈现全部数据”,它就是左表。
例如:
- “所有用户及其已支付订单金额” →
FROM users LEFT JOIN orders ON ... AND orders.status = 'paid' - “所有已支付订单及对应客户姓名” →
FROM orders INNER JOIN users ON orders.user_id = users.id
最容易被忽略的不是语法本身,而是ON和WHERE的分工边界——哪怕表结构、索引、数据都完美,一个条件位置放错,报表就少掉一半用户。










