SQL 中 WHERE IN 与 EXISTS 的使用场景

WHERE IN 与 EXISTS 的选择

今天重构了一段代码,遇到一个典型的场景:用 WHERE IN 还是 EXISTS?

场景

有一个缺陷记录表 A,需要筛选出满足某种工艺条件的记录。工艺信息存储在关联表 B 中。工艺只有3种类型。

原始实现(WHERE IN)

sql 复制代码
-- 第一步:查 B 表,拿到所有匹配的 ID
SELECT id FROM B WHERE process_name IN ('X', 'Y');
-- 结果可能几千个 ID

-- 第二步:把几千个 ID 塞进 IN
SELECT * FROM A WHERE b_id IN (id1, id2, ..., id3000);

问题在哪?

  • 两个 SQL 往返
  • 几千个 ID 从数据库传到 Java 内存,再传回去
  • IN 列表爆炸,SQL 包体巨大,解析慢
  • 数据量继续增长时性能断崖式下跌
  • A表可能压根就不需要where in 那么多条数据,如果有分页的话,它可能只需要查10条数据

重构后(EXISTS)

sql 复制代码
SELECT * FROM A WHERE EXISTS (
    SELECT 1 FROM B
    WHERE B.id = A.b_id
    AND B.process_name IN ('X', 'Y')
);

好处:

  • 单次查询,零数据传输
  • 数据库内部优化为 semi-join,走索引
  • 数据量越大优势越明显

什么场景用 IN?

IN 适合小集合、主表查询。比如根据用户选择的几个状态筛选订单、按几个固定值过滤。列表数量可控(几十个以内),IN 更直观,可读性好。

主表也有过滤条件时呢?

实际场景中主表往往不止关联表过滤,它自己也有筛选条件。EXISTS 天然和主表条件协同:

sql 复制代码
SELECT * FROM A 
WHERE A.warehouse_code = 'WH01'      -- 主表过滤
  AND A.created_at >= '2024-01-01'   -- 主表过滤
  AND A.b_id IN (id1, id2, ..., id3000);  -- 管你主表筛剩几条,先把 B 的 ID 全查出来再说

IN 的做法:先执行子查询查出所有 ID,再应用主表条件

sql 复制代码
SELECT * FROM A 
WHERE A.warehouse_code = 'WH01'      -- 主表过滤
  AND A.created_at >= '2024-01-01'   -- 主表过滤
  AND EXISTS (
    SELECT 1 FROM B
    WHERE B.id = A.b_id
    AND B.process_name IN ('X', 'Y')
  );

EXISTS 的做法:先应用主表条件缩小数据范围,再对缩小后的每一行检查 EXISTS

优化器会先走 warehouse_codecreated_at 的索引,只留下满足条件的少量行,然后对每一行执行 EXISTS 子查询(走关联索引)。主表条件越精确,EXISTS 优势越大。而 IN 不管主表筛得多干净,都得先把子查询全量结果集传给应用层再传回来。

实际项目中这个查询还带了仓库、时间范围、产线、次品原因等多个筛选条件,用 EXISTS 把 B 表关联过滤"打包"进主查询,是更合理的选择。

什么场景用 EXISTS?

EXISTS 适合关联子查询、大集合过滤。当 IN 列表可能膨胀到成百上千,甚至需要先查另一张表才能知道哪些值符合条件时,EXISTS 把过滤逻辑下推到数据库内部,避免了业务层的中间数据传输。

一句话

IN 适合你手上已经有一小撮值,"我就查这几个"。EXISTS 适合你只知道条件,"去关联表里查符合条件的所有记录"。

相关推荐
swipe3 分钟前
做多轮对话 Agent,为什么我建议把短期记忆放到 Redis
后端·面试·llm
程序员黑豆24 分钟前
AI全栈开发之Java:什么是JDK
前端·后端·ai编程
阿明在折腾1 小时前
从Canvas到AI模型:我在线工具站里的图片处理实战
前端·后端
tyung1 小时前
Go 手写 Wait-Free SPSC 无界队列:无 CAS、无锁、泛型节点池
数据结构·后端·go
Lucien3231 小时前
学完 Spring Boot 再看 FastAPI,我破防了
后端
小小龙学IT1 小时前
Go 语言后端开发:从并发模型到生产落地的工程实践
开发语言·后端·golang
程序员cxuan1 小时前
Agents.md 是什么
人工智能·后端·程序员
摇滚侠2 小时前
Java 零基础全套教程,类的加载过程与类加载器的理解,笔记 189
java·后端·intellij-idea
ServBay2 小时前
为什么我劝你不要在Mac上用Docker 进行本地 AI 开发
后端
蝎子莱莱爱打怪2 小时前
XZLL-IM干货系列 02|Protobuf 协议设计:从 JSON 切到二进制,每条消息省了 60%
后端·面试·架构