【MySQL篇】DEPENDENT SUBQUERY(依赖性子查询)优化:从百秒到秒级响应的四种优化办法

💫《博主介绍》:✨又是一天没白过,我是奈斯,从事IT领域✨

💫《擅长领域》:✌️擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控;并对SQLserver、NoSQL(MongoDB)有了解✌️

💖💖💖大佬们都喜欢静静的看文章,并且也会默默的点赞收藏加关注💖💖💖

    数据库中的慢查询,真的是让人抓狂!对于慢查询不仅会让数据库服务器的资源使用率瞬间飙升,甚至一个慢查询就能把 CPU内存IO 等资源几乎全部占满 ,并且还会导致后续的查询全部变慢,从而导致系统响应速度直接瘫了📉,所以优化慢SQL是一件非常非常重要的事情。

    优化慢 SQL 不仅需要对 SQL 语法了如指掌 🧠,还得熟悉MySQL中各种执行计划中的访问路径,比如全表扫描、索引扫描、临时表等。最近博主正好遇到一个经典的慢 SQL 问题,就是标题提到的 DEPENDENT SUBQUERY(依赖子查询) 。这个案例非常典型,优化过程也很有意思,所以整理出来分享给大家。

            

目录

生产 DEPENDENT SUBQUERY 案例分享:

优化DEPENDENT SUBQUERY方法一:改写成CTE(Common Table Expression,公用表表达式)(需要注意:CTE是MySQL 8.0.1版本中引入的,所以8.0以下的版本没有办法改写成CTE写法)

优化DEPENDENT SUBQUERY方法二:将 IN 子查询重写为 JOIN

优化DEPENDENT SUBQUERY方式三:使用 EXISTS 替代 IN 子查询

优化DEPENDENT SUBQUERY方法四:创建索引。无论使用上述哪种方法,确保相关列上有索引可以显著提升性能。


                   

    在开始之前先聊一聊 DEPENDENT SUBQUERY(依赖性子查询) 。在MySQL8.0官网文档中描述DEPENDENT SUBQUERY是执行计划中的select_type列的输出。

    在MySQL中, DEPENDENT SUBQUERY 是一种子查询类型,它的执行依赖于外部查询的每一行数据。这意味着,对于外部查询的每一行,子查询都会重新执行一次。这种类型的子查询通常会导致性能问题,尤其是在数据量较大的情况下。

           

DEPENDENT SUBQUERY 的工作原理

  • 依赖关系: DEPENDENT SUBQUERY 的子查询会引用外部查询中的列,因此它的执行结果依赖于外部查询的当前行。
  • 执行次数:对于外部查询的每一行,子查询都会重新执行一次。如果外部查询有 N 行,子查询就会执行 N 次。
  • 性能影响:由于子查询需要重复执行,这种类型的子查询通常会导致性能问题,尤其是在外部查询返回大量数据时。

    以下是一个简单的图示,帮助理解 DEPENDENT SUBQUERY的执行流程:

   

DEPENDENT SUBQUERY 案例:

SELECT *
FROM employees e
WHERE EXISTS (SELECT 1FROM departments dWHERE d.manager_id = e.employee_id
);

               

在这个查询中:

  • 外部查询从 employees 表中检索数据。
  • 子查询检查 departments 表中是否存在 manager_id 等于当前 employee_id 的记录。
  • 对于 employees 表中的每一行,子查询都会执行一次。

    如果执行计划中显示子查询的 select_type  DEPENDENT SUBQUERY ,这意味着子查询会为 employees 表中的每一行执行一次.这种案例很容易理解,因为在子查询中引用了外部查询中的表,所以子查询会为外部表中的每一行执行一次。

           


       

关于上述SQL DEPENDENT SUBQUERY 的执行流程:

  • 步骤一:外部查询开始执行。从 employees 表中读取第一行数据。
  • 步骤二:子查询执行。根据当前行的 employee_id ,在 departments 表中查找是否存在匹配的 manager_id 
  • 步骤三:返回结果。如果子查询返回结果,则外部查询的当前行被保留;否则,丢弃。
  • 步骤四:重复执行。外部查询继续读取下一行,重复上述步骤,直到所有行都被处理。

                         

生产 DEPENDENT SUBQUERY 案例分享:

    SQL中部分表名和字段有点敏感,所以博主用通用方式表达。这个SQL不长,并且逻辑也不复杂,硬是执行了 几百秒 都没有返回结果😰。liu_mysqloltp_ywcs_org表只有9万行数据,liu_mysqloltp_ywcs_cmn更是只有4092行数据。

SELECTa.Region_Name,t.* 
FROMliu_mysqloltp_ywcs_org t,liu_mysqloltp_ywcs_cmn a 
WHEREt.REGION_ID = a.id AND t.Hospital_Name IN ( SELECT tt.Hospital_Name FROM liu_mysqloltp_ywcs_org tt GROUP BY tt.Hospital_Name HAVING count(*) > 1 );

      

    通过explain查看一下这个SQL的执行计划,在这个查询中:

  • 外部查询:从 liu_mysqloltp_ywcs_org 表和 liu_mysqloltp_ywcs_cmn 表中选择数据。
  • 子查询:查找 liu_mysqloltp_ywcs_org 表中 Hospital_Name 出现次数大于 1 的记录。
  • 问题:子查询是 DEPENDENT SUBQUERY ,会为外部查询的每一行执行一次,导致性能低下。

    主要性能瓶颈是 DEPENDENT SUBQUERY 和全表扫描,可以看出有以下两个优化点:

  • PRIMARY 表 a 的扫描:从 ALL 变为 ref  range,减少扫描行数。

  • DEPENDENT SUBQUERY:被消除或优化为 JOIN ,减少子查询的执行次数。

                    

优化DEPENDENT SUBQUERY方法一:改写成CTE(Common Table Expression,公用表表达式)(需要注意:CTE是MySQL 8.0.1版本中引入的,所以8.0以下的版本没有办法改写成CTE写法)

    with as 是 SQL 中用于定义 CTE(Common Table Expression,公用表表达式) 的语法。它允许你在一个查询中定义一个临时的命名结果集,这个结果集可以在同一个查询中多次引用。WITH AS 的主要作用是提高查询的可读性和可维护性,尤其是在处理复杂查询时。

      

作用:把重复用到的sql语句放在with as里面,取一个别名,后面的查询就可以用它。对大批量的sql语句起到一个优化的作用,而且清楚明了

优点:可读性增强,比如对特定with子查询取个有意义的名字。With子查询只执行一次,将结果存储在用户临时表空间中,多次引用,增强性能

       

WITH DuplicateHospitals AS (SELECTtt.Hospital_NameFROMliu_mysqloltp_ywcs_org ttGROUP BYtt.Hospital_NameHAVINGCOUNT(*) > 1
)
SELECTa.Region_Name,t.*
FROMliu_mysqloltp_ywcs_org t
JOINliu_mysqloltp_ywcs_cmn a ON t.REGION_ID = a.id
JOIN
DuplicateHospitals dh ON t.Hospital_Name = dh.Hospital_Name;

优化点如下:

  • DuplicateHospitals 是一个 CTE,用于查找 liu_mysqloltp_ywcs_org 表中 Hospital_Name 出现次数大于 1 的记录。
  • 这个 CTE 只执行一次,结果会被缓存供后续查询使用。避免了原始查询中 IN  子查询的重复执行。
  • 主查询从 liu_mysqloltp_ywcs_org 表和 liu_mysqloltp_ywcs_cmn 表中选择数据。
  • 通过 JOIN DuplicateHospitals  CTE 与 liu_mysqloltp_ywcs_org 表关联,过滤出 Hospital_Name 出现次数大于 1 的记录。

                

优化DEPENDENT SUBQUERY方法二:将 IN 子查询重写为 JOIN

    可以将 IN 子查询重写为 JOIN ,避免 DEPENDENT SUBQUERY  IN 子查询可能会导致性能问题,尤其是在子查询返回的结果集较大时。可以尝试将子查询改写为 JOIN ,这样可以减少查询的复杂度。

SELECTa.Region_Name,t.*
FROMliu_mysqloltp_ywcs_org t
JOIN liu_mysqloltp_ywcs_cmn a ON t.REGION_ID = a.id
JOIN (SELECT tt.Hospital_NameFROM liu_mysqloltp_ywcs_org ttGROUP BY tt.Hospital_NameHAVING COUNT(*) > 1
) sub ON t.Hospital_Name = sub.Hospital_Name;

优化点如下:

  • 子查询被提取为一个派生表(sub),只执行一次。
  • 外部查询通过 JOIN 与派生表关联,避免了 DEPENDENT SUBQUERY

                    

优化DEPENDENT SUBQUERY方式三:使用 EXISTS 替代 IN 子查询

    EXISTS 通常比 IN 更高效,因为它可以在找到第一个匹配项后立即停止搜索。

SELECTa.Region_Name,t.* 
FROMliu_mysqloltp_ywcs_org t
JOINliu_mysqloltp_ywcs_cmn a 
ONt.REGION_ID = a.id 
WHEREEXISTS (SELECT 1 FROM liu_mysqloltp_ywcs_org tt WHERE tt.Hospital_Name = t.Hospital_Name GROUP BY tt.Hospital_Name HAVING COUNT(*) > 1); 

优化点如下:

  • EXISTS 子查询在找到第一个匹配项后就会停止搜索,避免了不必要的扫描。
  • 对于依赖子查询,EXISTS 通常比 IN 更高效,因为它不需要缓存结果集。
  • EXISTS 可以更好地利用索引,尤其是在子查询中使用了索引列时。

                        

优化DEPENDENT SUBQUERY方法四:创建索引。无论使用上述哪种方法,确保相关列上有索引可以显著提升性能。

  • 在 liu_mysqloltp_ywcs_org 表的 REGION_ID 列上创建索引:

CREATE INDEX idx_region_id ON liu_mysqloltp_ywcs_org(REGION_ID);
  • 在 liu_mysqloltp_ywcs_org 表的 Hospital_Name 列上创建索引:

  

CREATE INDEX idx_Hospital_Name ON liu_mysqloltp_ywcs_org(Hospital_Name);
  • 在 liu_mysqloltp_ywcs_cmn 表的 id 列上创建索引:
CREATE INDEX idx_id ON liu_mysqloltp_ywcs_cmn(id);

                                   

    关于 DEPENDENT SUBQUERY 的优化分享就到这里。 博主觉得这个案例真的是非常经典,堪称慢 SQL 优化的“教科书级”范例 📚。通过这次优化,不仅解决了性能瓶颈,还加深了对 MySQL 执行计划的理解。

    以后,博主还会继续分享更多有趣的慢 SQL 优化案例 ,比如索引失效、全表扫描、临时表滥用等。如果你也遇到过类似的“坑”,欢迎在评论区留言讨论 💬,一起交流学习,共同进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73296.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全文 - MLIR Toy Tutorial Chapter 1: Toy Language and AST

Toy 语言 本教程,将会借助一个玩具语言来讲解,这个语言我们称其为 Toy。Toy 是一个基于张量的语言,它允许你定义函数,执行一些数学计算,并且打印结果。做这样的设定,是因为我们希望让教程保持简明&#xff…

排序复习_代码纯享

头文件 #pragma once #include<iostream> #include<vector> #include<utility> using std::vector; using std::cout; using std::cin; using std::endl; using std::swap;//插入排序 //1、直接插入排序&#xff08;稳定&#xff09; void InsertSort(vecto…

CSS语言的双向链表

CSS语言的双向链表 引言 在计算机科学中&#xff0c;数据结构是一个极为重要的概念&#xff0c;而链表则是最常见的数据结构之一。链表可以分为单向链表和双向链表&#xff0c;其中双向链表因其灵活性和高效性而受到广泛应用。在前端开发的领域&#xff0c;尤其是CSS&#xf…

简单理解机器学习中top_k、top_p、temperature三个参数的作用

AI系列文章&#xff1a; AWS AI认证考试中经常提及几个重要的工具介绍 简单理解机器学习中top_k、top_p、temperature三个参数的作用 用Deepseek Kimi 快速生成高质量的ppt 在机器学习中&#xff0c;top_k、top_p 和 temperature 是用于控制生成模型&#xff08;如语言模型…

红宝书第十三讲:详解JavaScript核心对象:Array、Object、Date、RegExp

红宝书第十三讲&#xff1a;详解JavaScript核心对象&#xff1a;Array、Object、Date、RegExp 资料取自《JavaScript高级程序设计&#xff08;第5版&#xff09;》。 查看总目录&#xff1a;红宝书学习大纲 一、Object&#xff1a;万物皆对象的“盒子” Object是JavaScript中…

昆仑技术重构AI大模型落地范式,长期作“加法”迎来国产生态化“拐点”

作者 | 曾响铃 文 | 响铃说 DeepSeek的爆火&#xff0c;在业内迅速掀起了一场国产化的变革。“国产大模型国产算力”软硬协同的范式正在被重构&#xff0c;AI产业国产化的含金量持续提升&#xff0c;越来越多的企业在这一趋势下加速走上数智化转型路径。 其中&#xff0c;以…

原开源鸿蒙仓库停止更新

2月24日&#xff0c;gitee 上的开源鸿蒙组织&#xff0c;所有代码停止更新&#xff0c;查看代码仓显示已关闭&#xff0c;不少小伙伴以为停止更新了&#xff0c;发生了什么&#xff1f; 原因很简单&#xff0c;所有代码仓迁移至 Gitcode&#xff0c;至于为什么改用 Gitcode&…

Spring Boot框架中常用注解

以下是Spring Boot框架中常用注解的详细说明&#xff0c;包括名称、用途、用法、使用位置及扩展示例&#xff0c;按功能模块分类整理&#xff1a; 一、核心启动与配置注解 1. SpringBootApplication 用途&#xff1a;主启动类注解&#xff0c;整合了 Configuration、EnableAu…

Azure Delta Lake、Databricks和Event Hubs实现实时欺诈检测

设计Azure云架构方案实现Azure Delta Lake和Azure Databricks&#xff0c;结合 Azure Event Hubs/Kafka 摄入实时数据&#xff0c;通过 Delta Lake 实现 Exactly-Once 语义&#xff0c;实时欺诈检测&#xff08;流数据写入 Delta Lake&#xff0c;批处理模型实时更新&#xff0…

车载以太网网络测试 -23【TCPUDP通信示例】

1 摘要 在车载通信场景中&#xff0c;TCP以及UDP的通信可以用于多种应用&#xff0c;例如车辆状态监控、远程控制、数据采集等。以下是详细的代码示例&#xff0c;展示了如何使用Python实现简单的TCP客户端与服务端通信以及简单的UDP客户端与服务端通信&#xff0c;并模拟了车…

SpringBoot大学生竞赛管理系统设计与实现

一个用于管理大学生竞赛报名、信息查询与竞赛管理的系统&#xff0c;采用了现代化的SpringBoot框架进行开发。该系统的主要功能包括学生信息管理、教师信息管理、竞赛报名审核、竞赛信息管理等模块&#xff0c;适用于学校或教育机构进行竞赛活动的组织与管理。系统界面简洁&…

深入解析libsunrpc:构建分布式系统的核心RPC库

深入解析libsunrpc&#xff1a;构建分布式系统的核心RPC库 引言 在分布式系统开发中&#xff0c;远程过程调用&#xff08;Remote Procedure Call, RPC&#xff09; 是连接不同节点、实现跨网络服务调用的关键技术。作为SUN公司开源的经典RPC实现&#xff0c;libsunrpc 凭借其…

MinIO搭建部署

1、命令行安装 访问monio官网下载应用程序 # wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20250228095516.0.0-1.x86_64.rpm -O minio.rpm # sudo dnf install minio.rpm # mkdir ~/minio # minio server ~/minio --console-address :90012、dock…

Linux修改SSH端口号

我这里那RedHat系列的操作系统举例,修改SSH端口号 修改SSH配置文件:/etc/ssh/sshd_config,将端口号修改为2222.vim /etc/ssh/sshd_config重启SSH服务systemctl restart sshd# 如果是比较旧的OS,使用下面的命令重启 service ssh restart验证端口更改是否成功netstat -tulnp …

【嵌入式Linux】基于ArmLinux的智能垃圾分类系统项目

目录 1. 功能需求2. Python基础2.1 特点2.2 Python基础知识2.3 dict嵌套简单说明 3. C语言调用Python3.1 搭建编译环境3.2 直接调用python语句3.3 调用无参python函数3.4 调用有参python函数 4. 阿里云垃圾识别方案4.1 接入阿里云4.2 C语言调用阿里云Python接口 5. 香橙派使用摄…

【商城实战(63)】配送区域与运费设置全解析

【商城实战】专栏重磅来袭&#xff01;这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建&#xff0c;运用 uniapp、Element Plus、SpringBoot 搭建商城框架&#xff0c;到用户、商品、订单等核心模块开发&#xff0c;再到性能优化、安全加固、多端适配&#xf…

字节跳动实习生主导开发强化学习算法,助力大语言模型性能突破

目录 禹棋赢的背景与成就 主要成就 DAPO算法的技术细节 算法优势 禹棋赢的研究历程 关键时间节点 字节跳动的“Top Seed人才计划” 计划特点 小编总结 在大模型时代&#xff0c;经验不再是唯一的衡量标准&#xff0c;好奇心、执行力和对新技术的敏锐洞察力成为推动技术…

Rust + 时序数据库 TDengine:打造高性能时序数据处理利器

引言&#xff1a;为什么选择 TDengine 与 Rust&#xff1f; TDengine 是一款专为物联网、车联网、工业互联网等时序数据场景优化设计的开源时序数据库&#xff0c;支持高并发写入、高效查询及流式计算&#xff0c;通过“一个数据采集点一张表”与“超级表”的概念显著提升性能…

使用LangChain实现基于LLM和RAG的PDF问答系统

目录 前言一.大语言模型(LLM)1. 什么是LLM&#xff1f;2. LLM 的能力与特点 二、增强检索生成(RAG)三. 什么是 LangChain&#xff1f;1. LangChain 的核心功能2. LangChain 的优势3. LangChain 的应用场景4. 总结 四.使用 LangChain 实现基于 PDF 的问答系统 前言 本文将介绍 …

群核科技持续亏损近18亿:营销费用偏高,市场份额优势面临挑战

《港湾商业观察》施子夫 2025年开年&#xff0c;DeepSeek的爆火让大众将目光聚焦到了“杭州六小龙”。其中&#xff0c;杭州群核信息技术有限公司&#xff08;以下简称&#xff0c;群核科技&#xff09;因系“六小龙”中首家启动上市的公司而被外界更多关注。 在此次递表港交…