在 SQL 里,差集查询是指从一个集合里去掉另一个集合中包含的元素,也就是 A 减去 B。使用 NOT EXISTS 可以很自然地表达这个逻辑:对外层表 A 的每一行,去检查表 B 中是否存在满足条件的匹配行,如果不存在就说明这一行属于差集。

什么是 NOT EXISTS
NOT EXISTS 后面跟一个子查询。数据库会对外层查询的每一行执行一次子查询,如果子查询返回零行,NOT EXISTS 的结果为真,外层这行就被保留;如果子查询返回至少一行,结果为假,外层这行被过滤掉。
用 NOT EXISTS 实现 A 减 B
假设有两张表:users 表示所有用户,blacklist 表示黑名单用户。我们想找出不在黑名单里的用户,也就是 users 减去 blacklist。
-- 创建示例表
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE blacklist (
user_id INT PRIMARY KEY
);
-- 插入示例数据
INSERT INTO users VALUES (1, '张三'), (2, '李四'), (3, '王五');
INSERT INTO blacklist VALUES (2);
-- 使用 NOT EXISTS 查询差集:users - blacklist
SELECT u.id, u.name
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM blacklist b
WHERE b.user_id = u.id
);
上面语句中,子查询根据 u.id 去 blacklist 里找相同 user_id 的记录。对于 id 为 2 的李四,子查询能查到数据,NOT EXISTS 为假,被排除;张三和王五查不到,因此被选出。
关联条件要注意
写 NOT EXISTS 时,子查询里的关联条件必须写全,否则可能变成常量子查询,导致结果错误。例如下面这种写法就有问题:
-- 错误示例:子查询没有关联外层字段
SELECT u.id, u.name
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM blacklist b
WHERE b.user_id = 2
);
这个子查询跟 u 无关,只要 blacklist 里有 user_id 为 2 的记录,所有用户都会被过滤掉。
和 LEFT JOIN 写法的对比
LEFT JOIN 配合 IS NULL 也能做差集,但在语义上 NOT EXISTS 更直接。以下是对照:
| 写法 | 特点 |
|---|---|
| NOT EXISTS | 相关子查询,语义清晰,优化器常转为反连接 |
| LEFT JOIN + IS NULL | 先外连接再过滤,需注意连接字段为空的情况 |
小结
用 NOT EXISTS 实现差集查询 A 减 B,核心就是在 WHERE 子句里用子查询检查 B 中是否存在匹配,不存在就保留。只要关联条件写对,这种写法易读且可靠,是日常 SQL 查询中很实用的技巧。
SQLNOT_EXISTS差集查询修改时间:2026-07-27 12:21:17