别再滥用IN子查询了!用JOIN改写,从8秒到0.4秒(附优化步骤)

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS Agent(兼容OpenClaw),2核4GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 本文揭秘SQL子查询性能陷阱:IN慢因临时表+全量扫描;推荐JOIN改写——利用索引、避免磁盘IO。实测500万订单下,JOIN比IN快20倍!附三步改写法与NULL避坑指南。

我是小耶,干运营半路出家的野生DBA——写功课只是为了我踩过的坑,你们别再踩了!

刚学SQL那会儿,遇到“在A表里查B表也有的数据”,我总喜欢写 IN 子查询,因为好理解,像英语一样:user_id IN (SELECT user_id FROM orders)。后来有一次,我写了一个这样的查询,跑了快十分钟都没出结果,这才认真去研究它为什么慢。

子查询慢的原因,可以这样理解:就像你打电话给餐厅,让服务员把所有菜名念一遍(生成一个大列表),然后你拿着这个列表一样一样去找你想吃的。如果餐厅有几百道菜,这个过程会非常慢。

在数据库里,子查询会先产生一个临时结果集,可能放在内存或磁盘里,然后外层查询逐行去匹配。如果子查询返回几百万行,临时表巨大,内存放不下就会写磁盘,IO飙升,速度自然快不起来。

怎么改?能JOIN就别子查询。

举个例子:查询“下过单的用户”中的VIP用户。

❌ 较慢的写法(子查询):

SELECT * FROM users 
WHERE vip_level = 3 
  AND user_id IN (SELECT DISTINCT user_id FROM orders);

✅ 快得多的写法(JOIN):

SELECT DISTINCT u.* 
FROM users u 
JOIN orders o ON u.user_id = o.user_id 
WHERE u.vip_level = 3;

注意加了 DISTINCT,因为一个用户可能下多个订单,JOIN会产生重复,要去重。

为什么JOIN快?

  • 可以利用 orders.user_id 上的索引
  • 优化器会选择小表驱动大表(通常VIP用户数量较少)
  • 不会生成巨大的中间临时表

子查询什么时候还凑合?

  • 子查询的结果集非常小(比如只返回几十行),写起来简单,性能差别不大
  • EXISTS 在某些场景下比 IN 好,尤其是子查询大但外层能快速匹配时

特别提醒:NOT IN 要小心 NULL 值——如果子查询结果中包含 NULLNOT IN 会返回空结果,所以更推荐用 NOT EXISTS

实测数据
我拿一张500万行的订单表、50万行的用户表做了对比:

  • IN 子查询:8.3秒
  • JOIN 写法:0.4秒
    差距超过20倍。

改写三步骤

  1. 把子查询中的表放到 FROM 里,用 JOIN 连接
  2. 如果原SQL用了 DISTINCT 或担心重复,加上 DISTINCT 或用 GROUP BY
  3. 确保 JOIN 的关联字段有索引(例如 orders.user_id 要有索引)

学会用JOIN改写子查询,是SQL优化的进阶门槛。以后看到 INEXISTS,先问问自己:子查询结果集大不大?大就改JOIN。这个习惯能帮你省下很多加班时间。

小耶在手,SQL不愁。

你遇到过子查询跑崩的情况吗?评论区分享一下。

相关文章
|
NoSQL Java API
分布式系统---幂等性设计
分布式系统---幂等性设计
525 1
|
Kubernetes Cloud Native Java
Seata常见问题之回滚一直在重试如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
|
29天前
|
SQL 数据库
多表关联查询入门:LEFT JOIN、INNER JOIN一文搞懂|转行学DB第6天
本文通俗易懂地讲解了数据库多表查询的三种JOIN操作:INNER JOIN(内连接)只返回两表匹配的数据,适用于查询交集数据;LEFT JOIN(左连接)保留左表所有记录并匹配右表数据,适用于查询主表完整信息;RIGHT JOIN(右连接)则保留右表所有记录。
|
12月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
17天前
|
SQL 人工智能 安全
AI圈开始“养马”了?聊聊龙虾退位、爱马仕登基
AI智能体“龙虾”(OpenClaw)的衰落与“爱马仕”(Hermes Agent)的崛起:前者因API限策与高危漏洞(CVSS 9.9)式微;后者以持久记忆、技能自生成、跨平台互通等实用能力破圈,成技术圈新“拐杖”。但技术无银弹,懂你的工具才是真助力。
|
9月前
|
消息中间件 存储 缓存
再次了解kafka
Kafka通过offset机制解决消息重复消费问题,支持手动提交偏移量及唯一ID去重。它保证分区内的消息顺序消费,结合集群、副本与重平衡实现高可用。高性能设计包括顺序读写、分区、页缓存、零拷贝等。数据清理依赖保留时间或大小策略,点对点和发布订阅模式则通过消费者组实现。
|
9月前
|
消息中间件 Java Kafka
Java 事件驱动架构设计实战与 Kafka 生态系统组件实操全流程指南
本指南详解Java事件驱动架构与Kafka生态实操,涵盖环境搭建、事件模型定义、生产者与消费者实现、事件测试及高级特性,助你快速构建高可扩展分布式系统。
430 7
|
算法 关系型数据库 数据库
德哥的PostgreSQL私房菜 - 史上最屌PG资料合集
看完并理解这些文章,相信你会和我一样爱上PostgreSQL,并成为PostgreSQL的布道者。 沉稳的外表无法掩饰PG炙热的内心 。 扩展阅读,用心感受PostgreSQL 内核扩展 《找对业务G点, 体验酸爽 - PostgreSQL内核扩展指南》https://yq.
59765 152