SQL Academy · 课时

反连接与半连接(NOT EXISTS)

使用 EXISTS/NOT EXISTS 查找“ A 中在 B 中没有匹配项的行”(反连接)和“ A 中在 B 中至少有一个匹配项的行”(半连接)。

第 3 / 4 课14 个步骤

反连接与半连接(NOT EXISTS) 是 CoddyKit 上的免费 SQL Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Academy 课程共包含 4 节课。

半连接:“至少存在一个匹配项”

返回 A 中至少有一行与 B 匹配的行——但只返回 A 的列。SQL 通过 EXISTS 或 IN 实现半连接。

EXISTS 半连接

至少下过一个订单的用户:

SELECT u.* FROM users u
WHERE EXISTS (
  SELECT 1 FROM orders o WHERE o.user_id = u.id
);

IN 半连接

结果相同,但写法不同:

SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders);

为什么 EXISTS 通常更快

EXISTS 会提前结束:每个外层行找到第一个匹配项后就停止。IN 可能会将整个内部集合物化。现代规划器通常会将两者优化为相同的执行计划,但对于巨大的内部集合,EXISTS 是更稳妥的选择。

反连接:“没有匹配项”

从未下过订单的用户——有三种惯用写法:

-- NOT EXISTS (preferred):
SELECT u.* FROM users u
WHERE NOT EXISTS (
  SELECT 1 FROM orders o WHERE o.user_id = u.id
);

-- LEFT JOIN ... IS NULL:
SELECT u.* FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.id IS NULL;

-- NOT IN (risky with NULLs):
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);

为什么 NOT IN 有风险

如果内部子查询包含 NULL,NOT IN 会返回 NULL,而 WHERE 会丢弃谓词为 NULL 的行。结果是零行。NOT EXISTS 没有这个问题。

规划器优化

现代 PostgreSQL 能识别 EXISTS 和 NOT EXISTS 形式的半连接/反连接模式,并可能使用哈希连接执行:

EXPLAIN ANALYZE
SELECT u.* FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-- → Hash Anti Join

多列反连接

使用复合键:

SELECT * FROM order_items oi
WHERE NOT EXISTS (
  SELECT 1 FROM shipments s
  WHERE s.order_id = oi.order_id
    AND s.line_no  = oi.line_no
);

带附加谓词的 EXISTS 半连接

向内部子查询添加筛选条件:

SELECT u.* FROM users u
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.user_id = u.id
    AND o.status = 'paid'
    AND o.created_at >= NOW() - INTERVAL '30 days'
);

为关联列创建索引

对于 EXISTS 和 NOT EXISTS,内部子查询都会根据外层行的键进行筛选。如果该键没有索引,就会针对每个外层行执行一次扫描:

CREATE INDEX orders_user_id_idx ON orders(user_id);

何时使用 LEFT JOIN ... IS NULL

如果仪表板还需要未匹配一侧的列,LEFT JOIN 写法会很自然。对于纯粹的反连接语义,NOT EXISTS 更清晰。

超越 SQL:布隆过滤器

对于巨大的反连接,可以先使用布隆过滤器进行预筛选。PostgreSQL 通过 bloom 扩展支持 bloom 索引。

回顾

半连接 =“有匹配项”;反连接 =“没有匹配项”。

  • 半连接使用 EXISTS
  • 反连接使用 NOT EXISTS(对 NULL 安全)
  • 为关联列创建索引
  • 除非内部集合不可能包含 NULL,否则避免使用 NOT IN

快速检查

您想找出从未下过订单的用户。哪种 SQL 写法最安全、最符合惯例?

免费开始

用 AI 导师学习 SQL — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
46
课程
183

常见问题解答

「反连接与半连接(NOT EXISTS)」课时是免费的吗?

是的 — 「反连接与半连接(NOT EXISTS)」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Academy 课程的其余内容,请升级到 CoddyKit PRO。 SQL Academy 课程共包含 4 节课。

「反连接与半连接(NOT EXISTS)」这节课中我会学到什么?

使用 EXISTS/NOT EXISTS 查找“ A 中在 B 中没有匹配项的行”(反连接)和“ A 中在 B 中至少有一个匹配项的行”(半连接)。 你通过在浏览器中直接运行的动手代码来练习 SQL Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 SQL Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 SQL Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「反连接与半连接(NOT EXISTS)」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 SQL Academy 课中编写并运行代码吗?

能。每节 SQL Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 交叉连接与笛卡尔积
  2. 横向连接(LATERAL JOIN)
  3. 反连接与半连接(NOT EXISTS)
  4. 多表连接性能调优
← 返回 SQL Academy