anti join 是对“查找a表中存在但b表中不存在记录”逻辑的统称,sql标准中并无此关键字;实际用 left join ... where ... is null 或 not exists 实现,not exists 更安全可靠。

什么是 ANTI JOIN,SQL 里根本没有这个关键字
SQL 标准中没有 ANTI JOIN 语法。它只是对“查找在 A 表中存在、但在 B 表中不存在的记录”这类逻辑的统称。实际实现靠 LEFT JOIN ... WHERE ... IS NULL 或 NOT EXISTS,而不是某个叫 ANTI JOIN 的命令。误以为有这个语法,容易在写完 LEFT JOIN 后漏掉 WHERE 条件,结果返回全部左表数据——这是最常踩的坑。
用 LEFT JOIN + IS NULL 找孤立订单(外键失效场景)
典型场景:订单表 orders 的 customer_id 应该指向 customers 表,但某些订单的 customer_id 已被手动删掉或填错,导致查不到对应客户。这时要找出这些“孤儿订单”:
SELECT o.order_id, o.customer_id FROM orders o LEFT JOIN customers c ON o.customer_id = c.id WHERE c.id IS NULL;
-
LEFT JOIN必须写在WHERE前面,顺序不能反 -
WHERE c.id IS NULL是关键,不是o.customer_id IS NULL——后者找的是“没填客户 ID”的订单,不是“客户 ID 无效”的订单 - 如果
customers.id允许为NULL,这个写法会出错;此时必须用NOT EXISTS
用 NOT EXISTS 更安全地处理 NULL 外键列
当被关联字段本身可能为 NULL(比如 customers.id 是可空的),LEFT JOIN ... IS NULL 会把 NULL = NULL 当作匹配成功,漏掉本该算作“孤立”的记录。这时 NOT EXISTS 更可靠:
SELECT o.order_id, o.customer_id FROM orders o WHERE NOT EXISTS ( SELECT 1 FROM customers c WHERE c.id = o.customer_id );
-
NOT EXISTS对NULL值天然免疫:只要子查询没返回任何行,就判为真 - 子查询里用
SELECT 1而不是SELECT *,避免无谓字段传输开销 - 如果
customers.id有索引,这个写法通常比LEFT JOIN版本更快,尤其当右表很大但匹配率很低时
用 NOT IN 要特别小心三值逻辑
有人会写 WHERE o.customer_id NOT IN (SELECT id FROM customers),这在绝大多数情况下是错的:
- 只要
customers.id中任意一行是NULL,整个NOT IN表达式结果恒为UNKNOWN,最终查不到任何数据 -
NOT IN无法利用索引加速(多数引擎会退化为全表扫描) - 它等价于
o.customer_id != c1.id AND o.customer_id != c2.id AND ...,遇到NULL就崩 - 除非你 100% 确保子查询结果不含
NULL,否则别碰NOT IN
孤立数据的本质是语义断裂,不是语法技巧。重点永远在理解业务约束是否被破坏,而不是记住哪种写法“看起来更酷”。NOT EXISTS 在语义清晰性、NULL 安全性和执行稳定性上都更值得默认选用。










