首页/数据/SQL 速查/反连接:找出不存在于另一表的行

反连接:找出不存在于另一表的行

反连接三种写法:NOT EXISTS、NOT IN、LEFT JOIN IS NULL;语义与 NULL 行为各不相同,优先 NOT EXISTS。

SQL 速查数据更新于 2026-10-13

反连接是什么

反连接用来找出「不存在于另一张表」的行,常见三种写法: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 时判断列要选内表非空列(主键最佳),用可能为空的业务列判断会把真实匹配的行也当成未匹配。

易错点

  • 用 NOT IN 做反连接而内层列可空,结果整体为空且不报错
  • LEFT JOIN 加 IS NULL 时判断列选错,把已匹配的行误判为未匹配
  • 内表连接键没有索引,反连接退化为逐行全表扫描
  • 三种写法的执行计划差异很大,改写法前没有对比计划就上线
  • 反连接结果用于删除时未做二次确认,误删仍被引用的数据

数据来源与参考

  • MySQL 8.0 Reference Manual · Subqueries · NOT EXISTS
  • PostgreSQL Documentation · Subquery Expressions
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌