昌哥IT课堂|运维良方MySQL DEPENDENT SUBQUERY(相关子查询)原理详解

一、核心概念定义

DEPENDENT SUBQUERY 即相关子查询,是 MySQL 子查询的核心类型之一,核心特性如下:

•子查询无法独立执行,必须依赖外层查询当前行的字段值;

•执行逻辑为逐行联动:外层查询每读取一行数据,子查询就会执行一次;

•对应普通子查询(SUBQUERY):仅执行1次,结果全局缓存,外层直接复用,无重复执行开销。

在 EXPLAIN 执行计划 中,子查询行的 select_type 字段显示为 DEPENDENT SUBQUERY,即可判定为相关子查询。

二、实战示例SQL

需求:查询每个部门中,薪资高于本部门平均薪资的员工信息

sql
SELECT e1.name, e1.salary, e1.dept_id
FROM emp e1
WHERE e1.salary > (
    SELECT AVG(e2.salary) FROM emp e2 WHERE e2.dept_id = e1.dept_id
);

核心关联逻辑:子查询中 e1.dept_id 引用外层查询表字段,必须等待外层读取当前行数据后,才能执行子查询计算,因此形成依赖关系。

三、完整执行流程(流程图+步骤拆解)

3.1 可视化执行流程图

mermaid
flowchart LR
    A[开始] --> B[外层:读取e1第1行数据,获取dept_id值]
    B --> C[子查询:带入外层dept_id,计算对应部门平均薪资]
    C --> D[条件判断:当前员工薪资 > 部门平均薪资?]
    D -->|满足条件| E[保留当前行至结果集]
    D -->|不满足条件| F[丢弃当前行]
    E & F --> G[外层读取e1下一行数据,循环重复]
    G --> H[外层所有行数据读取完毕]
    H --> I[返回最终查询结果]

3.2 分步执行原理

1. 外层循环扫描:逐行读取外层表 e1 的数据,提取当前行的 dept_id 字段值;

2. 参数传递:将外层当前行的 dept_id 传入内层子查询,作为筛选条件;

3. 子查询单次执行:根据传入的部门ID,计算该部门的员工平均薪资;

4. 条件校验:对比当前员工薪资与部门平均薪资,筛选符合条件的数据;

5. 循环迭代:外层每切换一行数据,重复上述所有步骤,直至全表扫描完成。

核心性能痛点:外层表有 N 行数据,子查询就会执行 N 次,外层数据量越大,性能开销呈线性增长,无索引时极易出现性能雪崩。

四、具象数据演示

4.1 测试数据表(emp)

name(姓名)

salary(薪资)

dept_id(部门ID)

张三

10000

10

李四

8000

10

王五

15000

20

赵六

12000

20

4.2 逐行执行过程

1. 外层读取【张三】(dept_id=10)→ 子查询计算10部门平均薪资=9000 → 10000>9000,保留数据;

2. 外层读取【李四】(dept_id=10)→ 子查询重新计算10部门平均薪资=9000 → 8000<9000,丢弃数据;

3. 外层读取【王五】(dept_id=20)→ 子查询计算20部门平均薪资=13500 → 15000>13500,保留数据;

4. 外层读取【赵六】(dept_id=20)→ 子查询重新计算20部门平均薪资=13500 → 12000<13500,丢弃数据。

本次测试外层共4行数据,子查询累计执行4次,充分体现相关子查询的执行特性。

五、相关子查询 VS 普通子查询(SUBQUERY)

5.1 普通子查询示例

sql
SELECT * FROM emp WHERE dept_id = (SELECT dept_id FROM dept WHERE name='研发部');

5.2 核心区别对比

对比维度

DEPENDENT SUBQUERY(相关子查询)

SUBQUERY(普通子查询)

执行次数

外层N行,执行N次

仅执行1次

依赖关系

依赖外层当前行数据

独立执行,无外部依赖

结果缓存

无全局缓存,每次重新计算

结果全局缓存,全程复用

性能开销

大数据量下开销极高

开销极低

六、MySQL相关子查询性能问题与优化方案

6.1 常见性能坑点

•MySQL 旧版本中,IN+子查询 极易被优化为 DEPENDENT SUBQUERY,引发性能暴跌;

•EXISTS 语法底层会被优化为相关子查询,无索引时性能极差,有索引时可快速终止匹配,性能尚可;

•核心瓶颈:重复执行子查询,数据量越大,性能劣化越明显。

6.2 最优优化方案:改写为JOIN查询

将逐行执行的相关子查询,改写为一次性预计算+关联查询,彻底避免子查询重复执行。

sql
-- 优化后:预计算各部门平均薪资,再关联匹配数据
SELECT e.*
FROM emp e
INNER JOIN (
    SELECT dept_id, AVG(salary) avg_sal FROM emp GROUP BY dept_id
) t ON e.dept_id = t.dept_id
WHERE e.salary > t.avg_sal;

优化核心优势:子查询仅执行1次,预计算所有部门平均薪资,后续仅做表关联匹配,时间复杂度从 O(N*M) 降至 O(N+M)。

七、总结

1. DEPENDENT SUBQUERY 本质是行级联动查询,子查询依赖外层行数据、循环执行;

2. 性能短板极其明显,仅适用于小数据量场景,生产环境需尽量规避;

3. 最优优化思路:用 JOIN 替代相关子查询,预计算聚合结果,消除循环执行开销;

4. 日常排查慢SQL时,EXPLAIN 结果中出现大量 DEPENDENT SUBQUERY,优先进行优化改写。

字里行间,幸得你驻足品读。我是昌哥,欢迎关注与留言,共赴一场思想的碰撞。

-----------------------END-----------------------

各大平台都可以找到我:

————————————————————————————

公众号:昌哥知识星球

技术博客:http://www.linuxmysql.com

墨天轮:https://www.modb.pro/u/427810

CSDN :https://blog.csdn.net/rscpass

51CTO: https://blog.51cto.com/u_16068254

博客园:https://home.cnblogs.com/u/rscpass

知乎:https://www.zhihu.com/people/shukuinfo

掘金:https://juejin.cn/user/2801995051703454

百家号:https://author.baidu.com/home/1780697309880431

作者:阮胜昌

拥有:MySQL8.0 OCP、Oracle OCP、TIDB PCTA/PCTP/PCSD、Kingbase KCP,软考中级数据库系统工程师、RHCE7.0等行业认证

擅长主流数据库MySQL、Oracle、PostgreSQL的备份恢复,SQL调优、监控运维、故障应急处理等

可提供的技术服务:

1.数据库故障处理/疑难杂症远程支援

2.MySQL/PG/Oracle/SQLSERVER数据库技术服务

欢迎关注我的博客:http://www.linuxmysql.com 一起学习新知识!

昌哥IT课堂 开启薪未来|昌哥赋能 实战无忧 联系方式:rscpass (微信)

 

分割线
感谢打赏
江西数库信息技术有限公司
YWSOS.COM 平台代运维解决方案
 评论
 发表评论
姓   名:

Powered by AKCMS