left join + is null 是查主表中“从表无匹配记录”的标准解法,需用 left join 保留主表全部行,再通过 where 从表主键或非空外键 is null 过滤;不可用 = null、inner join 或 right join,且过滤条件必须在 where 而非 on 中。

LEFT JOIN + IS NULL 是标准解法
要查主表里“在从表中找不到匹配项”的记录,必须用 LEFT JOIN,再配合 WHERE 从表.主键 IS NULL。不能用 INNER JOIN 或 RIGHT JOIN,前者只返回有匹配的行,后者逻辑方向反了,容易绕晕。
常见错误是写成 WHERE 从表.字段 = NULL —— 这永远不成立,SQL 里判空必须用 IS NULL。
-
LEFT JOIN保证主表所有行都保留,从表无匹配则补NULL - 过滤条件必须写在
WHERE子句,不能写在ON后(否则会变成外连接变内连接) - 判断字段优先选从表的主键或非空唯一字段,避免因从表该字段本身允许
NULL导致误判
实际例子:查没下过单的用户
假设 users 表为主表,orders 表为从表,想找出从未下单的用户:
SELECT u.id, u.name FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.user_id IS NULL;
注意这里用 o.user_id IS NULL 而不是 o.id IS NULL,因为 orders.id 是自增主键,不可能为 NULL;而 o.user_id 是外键,缺失时才会是 NULL,语义更准确。
如果从表有复合外键(比如 (user_id, site_id)),需同时判断两个字段都为 NULL,但更稳妥的方式仍是用主键或唯一索引字段。
替代方案:NOT EXISTS 更清晰但稍慢
等价写法是 NOT EXISTS 子查询,语义更直白:“不存在对应订单的用户”:
SELECT u.id, u.name FROM users u WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.id );
多数场景下性能接近,但 NOT EXISTS 在从表有合适索引(如 orders(user_id))时可能略优;而 LEFT JOIN 在需要同时查从表其他字段(哪怕只是统计)时更灵活。
别用 NOT IN:如果 orders.user_id 里有 NULL,整个查询结果为空——这是经典陷阱。
JOIN 条件里加过滤会改变语义
有人想“只查某类订单缺失的用户”,于是把条件写进 ON:
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid'
这看似合理,实则查的是“没有已支付订单的用户”,包括那些只有未支付订单的用户——它没排除“有订单但都不是已支付”的情况。真要这样筛,得在 WHERE 里结合聚合或子查询处理,不能依赖 ON 的附加条件。
外连接的 ON 只控制“怎么连”,WHERE 才控制“最终留哪些行”。这个分界点,一不留神就漏数据。










