0Pricing
SQL Academy · 课时

EXISTS 与 NOT EXISTS

高效检查相关行

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

什么是 EXISTS?

EXISTS 运算符用于检查子查询是否至少返回一行。如果子查询产生任何结果,它的值就是 TRUE;如果子查询为空,则为 FALSE。

与其他用于比较值的子查询运算符不同,EXISTS 只关注是否存在——它不会查看子查询返回的实际数据。

设置示例表

在编写 EXISTS 查询之前,让我们创建两个表:customers 和 orders。在本课中,我们将通过这些表探索 EXISTS 和 NOT EXISTS 在实践中的工作方式。

CREATE TABLE customers (
  customer_id INT PRIMARY KEY,
  name        VARCHAR(100),
  country     VARCHAR(50)
);

CREATE TABLE orders (
  order_id    INT PRIMARY KEY,
  customer_id INT,
  amount      DECIMAL(10,2),
  order_date  DATE
);

INSERT INTO customers VALUES
  (1, 'Alice',   'US'),
  (2, 'Bob',     'UK'),
  (3, 'Charlie', 'US'),
  (4, 'Diana',   'DE');

INSERT INTO orders VALUES
  (101, 1, 250.00, '2024-01-10'),
  (102, 1, 180.00, '2024-02-15'),
  (103, 2,  95.00, '2024-03-01'),
  (104, 3, 430.00, '2024-03-22');

基本 EXISTS 语法

EXISTS 的基本语法是将其放在 WHERE 子句中。EXISTS 中的子查询通常会引用外层查询中的某一列——这称为相关子查询。

下面的查询会找出至少下过一个订单的每位客户。

SELECT customer_id, name
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
);

EXISTS 中的 SELECT 1

您可能已经注意到,子查询使用的是 SELECT 1,而不是选择某个实际列。这是有意为之的——EXISTS 只检查是否有行存在,而不关心这些行包含什么。

使用 SELECT 1(甚至使用 SELECT *)对结果没有影响,但 SELECT 1 能清楚地向数据库引擎和读者表明,具体值并不重要。

-- Both of these return the same result
SELECT name FROM customers c
WHERE EXISTS (SELECT 1   FROM orders o WHERE o.customer_id = c.customer_id);

SELECT name FROM customers c
WHERE EXISTS (SELECT o.* FROM orders o WHERE o.customer_id = c.customer_id);

数据库如何计算 EXISTS

对于外层查询中的每一行,数据库都会运行相关子查询。一旦找到一行匹配的行,引擎就会停止扫描,并将 EXISTS 标记为 TRUE——这种短路求值机制使 EXISTS 即使面对大型表也非常高效。

相比之下,JOIN 会在筛选之前构建完整的匹配行集合;当您只需要知道是否存在匹配项时,这可能会更慢。

-- EXISTS short-circuits after first match
-- Efficient even when orders table has millions of rows
SELECT name, country
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
    AND o.amount > 200
);

NOT EXISTS:查找不存在的行

NOT EXISTS 是相反的操作:当子查询找不到任何匹配行时,它会返回 TRUE。这是 SQL 中回答“哪些客户从未下过订单?”这类问题的标准方式。

尝试使用普通 JOIN 或 NOT IN 来解决这类问题时,如果涉及 NULL,可能会产生错误结果——NOT EXISTS 可以完全避免这一陷阱。

-- Customers who have NOT placed any order
SELECT customer_id, name
FROM customers c
WHERE NOT EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
);

涉及 NULL 时 NOT EXISTS 与 NOT IN 的比较

NOT EXISTS 相比 NOT IN 的一个关键优势是对 NULL 的安全性。如果 NOT IN 使用的子查询哪怕返回一个 NULL,整个 NOT IN 表达式就会变成 NULL——这意味着外层查询将不会返回任何行。

NOT EXISTS 不会受到这个问题的影响,因为它判断的是行是否存在,而不是值是否相等。

-- Dangerous: if any customer_id in orders is NULL,
-- NOT IN returns zero rows!
SELECT name FROM customers
WHERE customer_id NOT IN (SELECT customer_id FROM orders);

-- Safe: NOT EXISTS handles NULLs correctly
SELECT name FROM customers c
WHERE NOT EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.customer_id
);

包含多个条件的 EXISTS

EXISTS 内部的子查询可以包含任何有效的 SQL,包括多个 WHERE 条件。这样,您就可以检查高度具体的相关行,例如查找在特定月份下过金额超过某个阈值订单的客户。

-- Customers who placed an order over 200 in March 2024
SELECT c.name, c.country
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
    AND o.amount > 200
    AND o.order_date >= '2024-03-01'
    AND o.order_date <  '2024-04-01'
);

DELETE 和 UPDATE 中的 EXISTS

EXISTS 不仅限于 SELECT 语句。您可以在 UPDATE 和 DELETE 中使用它,根据另一个表中相关数据的存在情况来修改或删除行。

下面的示例会删除属于特定国家客户的订单。

-- Delete orders placed by US customers
DELETE FROM orders o
WHERE EXISTS (
  SELECT 1
  FROM customers c
  WHERE c.customer_id = o.customer_id
    AND c.country = 'US'
);

将 EXISTS 与 JOIN 进行比较

EXISTS 和 JOIN 通常可以表达同一个问题,但它们的行为不同。当存在多个匹配项时,JOIN 会产生重复行;而 EXISTS 对每个外层行最多只返回一次结果。

当您只需要知道某种关系是否存在,而不需要从相关表中获取数据时,EXISTS 更简洁,通常也更快。

-- JOIN may return duplicate customer rows if a customer has multiple orders
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

-- EXISTS always returns each customer once
SELECT c.name
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.customer_id
);

使用 NOT EXISTS 检查数据质量

NOT EXISTS 是进行数据质量审计的强大工具。您可以使用它查找孤立记录、缺失的引用,或本应具有关联数据却没有关联数据的行。

下面的查询会检测订单行中的 customer_id 是否没有匹配 customers 表中的任何行——这表明引用完整性遭到破坏。

-- Find orders with no matching customer (orphaned records)
SELECT o.order_id, o.customer_id, o.amount
FROM orders o
WHERE NOT EXISTS (
  SELECT 1
  FROM customers c
  WHERE c.customer_id = o.customer_id
);

快速检查

测试您对 EXISTS 和 NOT EXISTS 的理解。

课程回顾

在本课中,您学习了如何使用 EXISTS 和 NOT EXISTS,在不直接比较值的情况下检查相关行是否存在。

要点:

  • 当子查询找到一行匹配的行时,EXISTS 会立即返回 TRUE(短路求值)。
  • 当子查询找不到任何匹配行时,NOT EXISTS 会返回 TRUE。
  • 在 EXISTS 中使用 SELECT 1——返回的值并不重要。
  • NOT EXISTS 对 NULL 安全;NOT IN 则不安全——当可能出现 NULL 时,优先使用 NOT EXISTS。
  • EXISTS 可用于 SELECT、UPDATE 和 DELETE 语句。
  • 当您只需要检查是否存在时,EXISTS 通常比 JOIN 更简洁、更快。

常见问题解答

「EXISTS 与 NOT EXISTS」课时是免费的吗?

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

「EXISTS 与 NOT EXISTS」这节课中我会学到什么?

高效检查相关行 你通过在浏览器中直接运行的动手代码来练习 SQL Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 SQL Academy 需要有经验吗?

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

「EXISTS 与 NOT EXISTS」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. 相关子查询
  2. EXISTS 与 NOT EXISTS
  3. IN、ANY 与 ALL 对比
  4. EXISTS 与 JOIN 的性能对比
← 返回 SQL Academy