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

简介: 本文揭秘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 值——如果子查询结果中包含 NULL,NOT 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优化的进阶门槛。以后看到 IN、EXISTS,先问问自己:子查询结果集大不大?大就改JOIN。这个习惯能帮你省下很多加班时间。

小耶在手,SQL不愁。

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

相关文章
|
5月前
|
SQL 关系型数据库 MySQL
InnoDB锁机制分析:为什么没有索引的UPDATE会锁全表?
本文详解“无索引为何锁全表”:InnoDB行锁依赖索引,WHERE条件无索引→全表扫描→逐行加锁→等效表锁。附排查方法与5条保命优化建议。
|
4月前
|
人工智能 Cloud Native 关系型数据库
MySQL 8.4 LTS来了!从8.0到8.4,DBA必须知道的5个核心变化
MySQL 8.0社区版将于2026年结束生命周期,8.4 LTS作为首个长期支持版本,提供5年超长支持周期(至2031年)。本文从InnoDB并行查询、Redo Log动态容量、默认认证插件变更、参数默认值调整、云原生适配五个维度,梳理DBA升级前必须掌握的核心变化,并提供升级检查清单。
|
5月前
|
SQL 关系型数据库 MySQL
MySQL主从复制实战:从原理到读写分离,新手避坑全指南
数据库小学妹带你轻松入门主从复制!✅基于binlog实现主库写、从库读,支撑读写分离与高可用;🛡️保障数据安全(灾备)、提升并发能力;🔧详解三种复制模式、搭建步骤、延迟优化及避坑指南。运维进阶必备!
|
5月前
|
SQL 关系型数据库 MySQL
一张5000万行的表,加索引从45秒到0.02秒——索引设计你真的会吗
本文实测5000万订单表:无索引查询45秒,加索引后仅0.02秒(提升2250倍)。详解索引原理、建索引时机、联合索引最左前缀、覆盖索引及隐式转换陷阱,干货不啰嗦!
|
5月前
|
SQL 缓存 数据库
你还在用LIMIT 1000000,10?献上分页查询优化技巧
本文详解“深分页”陷阱:`LIMIT 1000000,10`为何慢?3种优化方案(游标法、子查询定位、延迟关联)实测提速数十倍,助你零成本提升SQL性能!
|
5月前
|
SQL 运维 关系型数据库
DBA必备技能:MySQL误删恢复完全指南(全量备份+binlog回放)
本文详解误删数据(如`DELETE FROM orders`)后的紧急恢复三步法:查Binlog→临时库回放→差异导回,并附4条血泪预防措施。不讲段子,只教能救命的操作!
|
6月前
|
SQL 关系型数据库 MySQL
EXPLAIN 执行计划:一眼看穿你的SQL慢在哪
数据库小学妹带你轻松掌握SQL性能诊断!通过EXPLAIN查看执行计划,精准识别索引失效、全表扫描(ALL)、key为NULL等瓶颈。聚焦type、key、rows等6个关键字段,结合实战案例与避坑指南(如函数滥用、最左前缀破坏),让优化有的放矢。学完即用,告别盲目调优!
|
6月前
|
存储 人工智能 弹性计算
阿里云新用户、老用户与企业用户定义及优惠活动政策解析
本文系统梳理阿里云新用户、老用户及企业用户的定义标准,深度解析“免费试用+首购特惠+续费同价+企业专项补贴”等差异化优惠政策,并提供实操避坑指南,助力用户精准选配、降本增效。
854 2
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
数据可视化 测试技术 API
从接口性能到稳定性:这些API调试工具,让你的开发过程事半功倍
在软件开发中,接口调试与测试对接口性能、稳定性、准确性及团队协作至关重要。随着开发节奏加快,传统方式已难满足需求,专业API工具成为首选。本文介绍了Apifox、Postman、YApi、SoapUI、JMeter、Swagger等主流工具,对比其功能与适用场景,并推荐Apifox作为集成度高、支持中文、可视化强的一体化解决方案,助力提升API开发与测试效率。