← 返回题目列表

SQL 中 IN 和 EXISTS 有什么区别?

高频 中等 第 13 / 28 题 更新于 2026/07/27
SQLINEXISTS子查询

简化版

IN 判断左侧值是否属于一个值集合,EXISTS 只判断相关子查询是否至少返回一行。现代数据库可能把两者优化成相近的半连接计划,因此不能断言谁一定更快;选择时先看语义,并特别注意 NOT IN 遇到 NULL 时可能返回 UNKNOWN。

详细版

下面两种写法都可查询有订单的客户:

SELECT * FROM customer
WHERE id IN (SELECT customer_id FROM orders);

SELECT * FROM customer AS c
WHERE EXISTS (
  SELECT 1 FROM orders AS o WHERE o.customer_id = c.id
);

EXISTS 子查询的输出列不参与结果,只关心是否存在匹配行,所以习惯写 SELECT 1;写 SELECT * 通常也不改变 EXISTS 的真假。IN 更像集合成员判断,适合短常量列表和直接表达值集合。

完整版教学

一、IN 表达“属于集合”

WHERE status IN ('paid', 'shipped')

右侧可以是常量列表,也可以是返回一列的子查询。子查询返回多列时不能与单个标量直接比较。重复值不会改变 IN 的真假,因为成员关系只关心有没有相等值。

二、EXISTS 表达“是否存在匹配行”

SELECT c.id, c.name
FROM customer AS c
WHERE EXISTS (
  SELECT 1
  FROM orders AS o
  WHERE o.customer_id = c.id
    AND o.status = 'paid'
);

子查询引用了外层的 c.id,属于相关子查询。逻辑上对当前客户判断是否至少存在一笔已支付订单。数据库不需要把子查询中的具体列返回给外层。

三、为什么不能背“外表小用 IN,子表小用 EXISTS”

这是把旧版本、特定执行计划经验当成固定规律。MySQL 等现代优化器可以把 INEXISTS 转换为半连接,也可能选择物化、索引查找等策略;最终计划由统计信息、索引、数据分布和版本共同决定。

面试中更准确的回答是:两者语义不同但很多等价场景可被优化为相似计划,性能要用 EXPLAIN 和真实数据验证。关联列上是否有合适索引往往比关键字选择更重要。

四、NOT IN 与 NULL 是最大陷阱

SELECT * FROM customer
WHERE id NOT IN (SELECT customer_id FROM blacklist);

如果子查询结果含 NULL,对普通 id 而言,“不等于 NULL”得到 UNKNOWN,整个 NOT IN 条件可能没有任何行满足。更稳妥的反匹配写法是:

SELECT * FROM customer AS c
WHERE NOT EXISTS (
  SELECT 1 FROM blacklist AS b WHERE b.customer_id = c.id
);

也可以在 NOT IN 子查询中明确排除 NULL,但仍要确认左侧值自身的空值语义。

五、如何选择

  • 固定少量取值:IN (...) 简洁直观;
  • 强调是否存在关联记录:EXISTS 更贴合语义;
  • 反关联且空值不可控:优先考虑 NOT EXISTS
  • 关心性能:检查索引、统计信息和执行计划,不凭口诀改写。

六、常见误区与追问

写法语义关注点高风险点
IN左值是否属于右侧集合右侧集合列数、类型、重复值
EXISTS相关条件是否至少匹配一行关联条件和索引是否合适
NOT IN左值是否不属于集合右侧出现 NULL
NOT EXISTS不存在匹配行反关联语义更稳定
  • 误区:IN 和 EXISTS 谁快有固定答案。 现代优化器可能把二者改写成半连接、物化子查询或索引查找,真实性能要看执行计划和数据分布。
  • 误区:EXISTS 子查询里 SELECT 什么会影响结果。 EXISTS 只关心是否返回至少一行,SELECT 1 是表达习惯,不是性能魔法。
  • 误区:NOT IN 和 NOT EXISTS 完全等价。 NOT IN 遇到右侧集合含 NULL 时会受三值逻辑影响,可能导致没有任何行满足条件。
  • 追问:什么时候更适合写 IN? 固定少量枚举值、语义上就是集合成员判断时,IN (...) 简洁清楚。
  • 追问:什么时候更适合写 EXISTS? 强调外层行是否存在关联记录,尤其是相关子查询、反关联和可空列风险场景,EXISTS/NOT EXISTS 更直观。
  • 追问:优化这类 SQL 先看什么? 先看关联列索引、统计信息、子查询是否可去重或物化,再用 EXPLAIN 验证计划,而不是只替换关键字。

易错点:NOT IN 的右侧集合只要混入 NULL,三值逻辑就可能让反匹配结果和直觉完全不同。

七、加强记忆

IN 问“值在不在集合里”,EXISTS 问“匹配行存不存在”。等价查询可能被优化器变成同类半连接,谁快没有固定答案;真正必须记住的是 SQL 的三值逻辑——NOT IN 的集合只要混入 NULL,就可能让结果全部变成 UNKNOWN。