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

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

什么场景用 EXISTS?

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

一句话

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

相关推荐
Rain的Java大神之路14 分钟前
面了 50 个候选人,90% 都挂在 LeetCode 3 无重复字符的最长子串:滑动窗口的 5 个致命坑
java·后端·leetcode
狂师43 分钟前
夯爆了!分享几个我在用的,基于业务场景接口串联测试Skill !
人工智能·后端·测试
北京地铁1号线1 小时前
后端开发:缓存(Cache)简介
后端·缓存·cache
IT_陈寒1 小时前
Vue项目实战经验分享:动态组件在Keep-Alive下的缓存陷阱
前端·人工智能·后端
程序猿乐锅1 小时前
【从0-1学习Python | 第九篇】继承
开发语言·后端·python·学习·fastapi
2601_954005621 小时前
11 种触发 × 18 种行为:ricon 交互编排系统全解
前端·后端·物联网·数学建模·信息可视化·交互
会编程的吕洞宾1 小时前
Spring AI 语义缓存:重复提问不重复花钱
java·人工智能·后端·spring
郭小李2 小时前
spring @RequestMapping注解
java·后端·spring
喵了几个咪11 小时前
GoWind CMS 拆解:在 Go 里给 TipTap 搭一套 Yjs 协同后端
开发语言·后端·golang
Flynt11 小时前
我把微信运动的排行榜和定点推送复刻了一遍:100万次步数更新只要4秒,坑在别处
redis·后端·性能优化