反连接是什么
反连接用来找出「不存在于另一张表」的行,常见三种写法:NOT EXISTS、NOT IN、LEFT JOIN 加 IS NULL。三者语义与 NULL 行为不同:NOT EXISTS 与 NULL 无关,最稳妥;NOT IN 只要子查询里有 NULL 就返回空结果;LEFT JOIN 加 IS NULL 可读性一般但性能通常不错。选择依据是语义清晰度与内表索引情况,NOT EXISTS 一般是首选,需要进一步看计划再决定是否换写法。
基本信息
| NOT EXISTS | 推荐写法,语义不受 NULL 影响 |
|---|---|
| NOT IN | 子查询含 NULL 时结果整体为空 |
| LEFT JOIN + IS NULL | 性能常不错,判断列选内表主键最稳 |
| 必要条件 | 内表连接键上要有索引,否则退化为逐行扫描 |
| NULL 安全性 | 只有 NOT EXISTS 与 LEFT JOIN 天然安全 |
| 适用平台 | 三种写法 MySQL 8.0 与 PostgreSQL 都支持 |
典型使用场景
- 找未下单用户:用户表里找不到对应的订单记录
- 找未关联数据:工单表里没有匹配到责任人的记录
- 数据一致性核对:源表有而目标表没有的记录
- 清理孤儿数据的前置查询:先查出候选项再决定删除
- 打标:把从未登录过的账号标记为休眠账号
- 对账:找出只在旧系统里存在的账号
三种写法
三种写法
-- 写法一:NOT EXISTS(推荐)
SELECT u.id FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-- 写法二:LEFT JOIN 加 IS NULL
SELECT u.id FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.id IS NULL; # 用内表主键判断最保险
-- 写法三:NOT IN(子查询列可空时有坑)
SELECT u.id FROM users u
WHERE u.id NOT IN (SELECT user_id FROM orders WHERE user_id IS NOT NULL);三种写法对照
| 写法 | NULL 行为 | 可读性与性能 |
|---|---|---|
| NOT EXISTS | 不受影响 | 语义清晰,通常最优 |
| NOT IN | 含 NULL 时结果为空 | 写法简洁但危险 |
| LEFT JOIN + IS NULL | 不受影响 | 略绕,计划稳定 |
| EXCEPT / MINUS | 集合运算,不受影响 | 可读性好,部分数据库支持 |
三种写法的执行计划可能不同:小表驱动时 LEFT JOIN 与 NOT EXISTS 接近,大表场景要看内表索引是否被用上,比较后再定。
NOT IN 遇到可空子查询列会静默返回空结果,反连接需求优先用 NOT EXISTS,避免这类难以定位的语义问题。
外连接加 IS NULL 时判断列要选内表非空列(主键最佳),用可能为空的业务列判断会把真实匹配的行也当成未匹配。