left join后筛右表无匹配记录需用where右表字段is null,不可用= null;on中加右表条件会改变语义;性能上is null难走索引,可改用not exists;多表时各null判断须明确归属且逻辑不可混用。

LEFT JOIN后WHERE右表字段IS NULL的写法
LEFT JOIN本身不会过滤数据,要筛出右表没匹配上的记录,必须在WHERE子句中显式判断右表字段为NULL。注意不能用= NULL——SQL里NULL只能用IS NULL或IS NOT NULL判断。
常见错误是写成WHERE right_table.id = NULL,这永远返回空结果,因为NULL = NULL不成立。
示例:查所有没有订单的用户
SELECT u.id, u.name FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.user_id IS NULL;
ON条件里加右表限制会改变JOIN语义
如果把过滤条件写进ON子句(比如ON u.id = o.user_id AND o.status = 'paid'),LEFT JOIN仍会保留左表所有行,只是右表匹配失败时字段为NULL。这时WHERE o.user_id IS NULL筛出的是“左表行 + 右表完全没匹配上(含status不为'paid'的也视作不匹配)”的记录,逻辑和预期可能不符。
想严格筛选“有订单但状态不是paid”的用户,应该用INNER JOIN配合WHERE;而“没订单”的场景,ON里不该加右表业务条件。
-
ON u.id = o.user_id→ 正确:只关心关联关系 -
ON u.id = o.user_id AND o.deleted = 0→ 风险:软删除标记会影响IS NULL判断结果
性能与索引注意事项
当右表数据量大时,WHERE right_table.xxx IS NULL无法利用右表的普通索引(因为NULL值通常不被B-tree索引存储),可能导致全表扫描。
优化方向:
- 确保
ON条件中的关联字段(如o.user_id)有索引 - 若业务允许,用
NOT EXISTS替代:SELECT u.* FROM users u WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.id );
某些场景下执行计划更优 - 避免在
WHERE里对右表字段做函数操作,如WHERE COALESCE(o.user_id, 0) IS NULL会失效索引
多表LEFT JOIN时NULL判断容易混淆
连表超过两层时,每个右表的IS NULL判断必须明确指定字段归属,否则可能误判。例如:
SELECT u.id, o.id, p.id FROM users u LEFT JOIN orders o ON u.id = o.user_id LEFT JOIN products p ON o.id = p.order_id WHERE o.id IS NULL OR p.id IS NULL;
这里o.id IS NULL表示用户无订单,p.id IS NULL表示订单无商品——两者逻辑不同,不能合并条件。
容易忽略的是:当o.id IS NULL为真时,p.order_id必然为NULL(因为o没数据,p自然没机会关联),所以p.id IS NULL在此分支下恒成立,实际效果不是“用户无订单 或 订单无商品”,而是“用户无订单 或 (用户有订单且该订单无商品)”。需要按真实业务意图拆开写。










