NOT EXISTS 比 LEFT JOIN + IS NULL 更安全,因其不依赖连接字段的 NULL 语义,避免因 A 表连接字段为 NULL 导致误判;且比 NOT IN 更可靠,因后者在 B 表匹配字段含 NULL 时整个条件恒为 UNKNOWN 而返回空结果。
NOT EXISTS 为什么比 LEFT JOIN + IS NULL 更安全
因为 NOT EXISTS 只关心子查询是否返回任意一行,不依赖连接字段的 NULL 值语义,避免了 LEFT JOIN 在连接字段本身含 NULL 时误判(比如 A 表某行 id 是 NULL,B 表没有 NULL 对应值,LEFT JOIN ... IS NULL 会错误地把它算进差集)。
实际写法中,NOT EXISTS 的子查询必须关联外层,否则变成恒真或恒假——这是新手最常漏掉的点。
标准写法:A 表中不在 B 表里的记录
假设 A 和 B 都有 id 字段,要查 A-B:
SELECT * FROM A WHERE NOT EXISTS ( SELECT 1 FROM B WHERE B.id = A.id );
关键细节:
-
SELECT 1是惯用写法,子查询里选什么都行,但不能写SELECT *(语义无错,但可读性差) - 子查询中的
B.id = A.id是**相关子查询**的锚点,缺了它就查出全量 A 或空结果 - 如果匹配字段是复合键(如
(user_id, order_date)),必须在子查询中全部写出:WHERE B.user_id = A.user_id AND B.order_date = A.order_date
性能陷阱:没加索引时 NOT EXISTS 会很慢
NOT EXISTS 的执行效率高度依赖子查询条件字段是否有索引。若 B.id 没索引,数据库可能对 A 的每一行都全表扫描 B。