SQL 反连接是什么?NOT EXISTS、LEFT JOIN IS NULL 和 NOT IN 怎么选?
简化版
反连接用于查询“在 A 中存在,但在 B 中不存在”的数据。常见写法有 NOT EXISTS、LEFT JOIN ... IS NULL 和 NOT IN。一般推荐优先使用 NOT EXISTS,语义清晰且对 NULL 更安全;LEFT JOIN IS NULL 也常用;NOT IN 遇到子查询结果包含 NULL 时可能返回意外结果。
详细版
例如要查没有下过单的用户,本质就是用户表反连接订单表。NOT EXISTS 会对每个用户判断是否不存在匹配订单;LEFT JOIN IS NULL 先左连接,再筛选右表为空的行;NOT IN 则判断用户 id 不在一个集合里。
三者在很多数据库里可能被优化成相似执行计划,但语义细节不同。最大坑点是 NOT IN 和 NULL:如果子查询集合中出现 NULL,三值逻辑会让比较结果变成 UNKNOWN,最终可能查不出任何行。
面试要能写出 SQL,也要解释为什么 NULL 会影响 NOT IN。
完整版教学
一、反连接的业务语义
查没有订单的用户。
查没有绑定角色的账号。
查没有库存记录的商品。
查未完成某步骤的流程实例。
这些都属于“主表中找不到匹配记录”。
二、NOT EXISTS 写法
SELECT u.*
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.user_id = u.id
);
这段 SQL 的语义是:不存在任何一条订单属于当前用户。
SELECT 1 只是占位,数据库关注是否存在匹配行。
三、LEFT JOIN IS NULL 写法
SELECT u.*
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.user_id IS NULL;
它先保留所有用户,再把没有匹配订单的用户筛出来。
筛选的列要选择右表中用于匹配且不会无故为 NULL 的列。
四、NOT IN 的 NULL 问题
SELECT *
FROM users
WHERE id NOT IN (SELECT user_id FROM orders);
如果 orders.user_id 中有 NULL,id NOT IN (...) 的判断可能变成 UNKNOWN。
SQL 的 WHERE 只保留 TRUE,不保留 UNKNOWN。
所以结果可能为空或不符合预期。
五、三种写法对比
| 写法 | 优点 | 风险 |
|---|---|---|
NOT EXISTS | NULL 安全,语义清晰 | 相关子查询写法稍长 |
LEFT JOIN IS NULL | 直观,常见 | 条件放错会变成错误结果 |
NOT IN | 简短 | 子查询含 NULL 时危险 |
EXCEPT | 集合语义清晰 | 兼容性和去重语义要注意 |
六、性能和索引
反连接通常需要右表关联列有索引。
例如 orders(user_id)。
没有索引时,数据库可能需要大量扫描。
现代优化器常能把 NOT EXISTS 改写为 anti join。
但复杂条件、函数表达式、类型转换会影响优化。
面试里不要只背写法,最好补一句“右表匹配列要建索引,并检查执行计划”。
七、误区和追问
- 误区:NOT IN 和 NOT EXISTS 永远等价。 子查询含 NULL 时,
NOT IN的结果可能完全不同。 - 误区:LEFT JOIN 后筛右表任意列 IS NULL 都可以。 应选择能代表匹配是否存在的非空关联列。
- 误区:反连接一定很慢。 有合适索引和优化器改写时可以很高效。
- 追问:为什么 NOT IN 遇到 NULL 危险? 因为 SQL 三值逻辑中比较可能得到 UNKNOWN,WHERE 不会保留 UNKNOWN。
- 追问:查没有订单但用户状态正常怎么写? 用户状态条件放主查询,订单匹配条件放子查询或 ON 中。
- 追问:EXCEPT 能不能做反连接? 可以表达集合差,但要注意去重、字段一致和数据库支持。
八、面试收束
回答时优先给 NOT EXISTS。
再说明 LEFT JOIN IS NULL 的可替代性和 NOT IN 的 NULL 风险。
最后补上索引和执行计划,这样答案比较完整。
九、常见误区与追问
- 误区:只记住 SQL 反连接是什么?NOT EXISTS、LEFT JOIN IS NULL 和 NOT IN 怎么选? 的结论就够了。 数据库题通常还要解释索引、事务、锁、执行计划或一致性边界,否则很容易被追问打穿。
- 误区:能查出结果就说明 SQL 或设计没问题。 还要看数据量扩大到 100 万行后是否仍能走合适索引、是否产生临时表或锁等待。
- 误区:所有场景都追求强一致。 读写分离、缓存、异步任务都可能牺牲一部分实时性,关键是说明业务是否允许。
- 追问:线上变慢时你先看什么? 先看慢 SQL、执行计划、扫描行数、锁等待和连接池,再判断是 SQL 写法、索引还是并发问题。
- 追问:如何证明这个方案可落地? 给出一个小数据例子,再补充约束、失败场景和回滚方案,避免只停留在概念层。
十、加强记忆
记 SQL 反连接是什么?NOT EXISTS、LEFT JOIN IS NULL 和 NOT IN 怎么选? 时,把它压成“语义正确、执行高效、并发安全”三件事。先说清这个知识点解决什么数据库问题,再用 1 个带数字的小例子说明数据量一大为什么会出差异,最后补上索引、锁、事务或执行计划里的易错点。这样面试官无论追问 SQL 写法、线上慢查,还是并发一致性,你都能沿着同一条线继续展开。