0

0

mysql相关子查询和非相关子查询有何不同

P粉602998670

P粉602998670

发布时间:2025-09-23 11:04:05

|

374人浏览过

|

来源于php中文网

原创

非相关子查询独立执行且仅运行一次,结果固定;相关子查询依赖外部查询的列,对外部每行数据重新执行,性能开销大。

mysql相关子查询和非相关子查询有何不同

MySQL中相关子查询和非相关子查询的核心区别,在于它们对外部查询的依赖关系以及执行方式。简单来说,非相关子查询是“独立思考”的,它自己就能跑出结果,然后把结果交给外部查询使用;而相关子查询则需要“看外部查询的脸色”,它会为外部查询的每一行数据重新计算一次,结果也因此动态变化。

非相关子查询,顾名思义,它不依赖于外部查询的任何列。当MySQL执行这样的语句时,子查询会先独立执行一次,生成一个结果集(或者一个标量值),然后这个结果集会被外部查询当作一个固定的数据源来使用。你可以把它想象成一个独立的函数,先计算出结果,再把结果传给主程序。这种情况下,子查询通常只执行一次,效率相对较高。

相关子查询则完全不同。它的WHERE子句(或其他部分)中会引用外部查询的列。这意味着,对于外部查询的每一行数据,MySQL都会重新执行一次这个子查询。每次执行时,子查询都会根据当前外部查询行的值来过滤或计算。这就像一个循环,外部查询每处理一行,子查询就跟着跑一次。正因为这种“行对行”的依赖关系,相关子查询的性能开销通常会比非相关子查询大很多,尤其是在数据量大的时候。

如何判断一个子查询是相关还是非相关?

判断一个子查询是相关还是非相关,其实并不复杂,关键在于观察子查询内部是否引用了外部查询的列。

最直接的判断方法就是看子查询的WHERE子句或其他条件部分。如果子查询的WHERE条件中出现了外部查询表(或者别名)的列,那么它就是一个相关子查询。反之,如果子查询可以完全独立地执行,不依赖外部查询的任何信息,那么它就是非相关子查询。

举个例子:

非相关子查询示例:

SELECT employee_name
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

在这个例子中,SELECT AVG(salary) FROM employees 这个子查询可以独立运行,它会计算出所有员工的平均工资,然后外部查询会用这个平均工资来筛选员工。子查询只执行一次。

相关子查询示例:

SELECT department_name
FROM departments d
WHERE EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.department_id);

这里,SELECT 1 FROM employees e WHERE e.department_id = d.department_id 这个子查询内部引用了外部查询 departments 表的 d.department_id。对于 departments 表的每一行,子查询都会重新执行一次,检查是否存在对应部门的员工。这就是一个典型的相关子查询。

有时候,我们也会看到一些复杂的嵌套查询,判断起来可能稍微有点绕,但核心原则不变:只要子查询的执行逻辑依赖于外部查询的当前行数据,它就是相关的。

什么时候应该使用相关子查询,什么时候应该避免?

相关子查询并非一无是处,它在某些场景下能简洁明了地表达复杂的业务逻辑,但其潜在的性能问题也确实让人头疼。

什么时候应该使用(或考虑使用)相关子查询:

淘宝互刷平台刷信誉源码
淘宝互刷平台刷信誉源码

淘宝互刷平台刷信誉源码主要特性:1、系统采用国内著名CMS内核做为基础模块化开发,继承CMS原有强大功能之外,同时拓展任务模块、快递单模块、会员模块、信用评价模块等多个相关模块,支持生成HTML静态和动态ASP,有效的提高了系统的性能,不仅减轻服务器的负载提高搜索收录率,增加网站收录。2、系统主要由淘宝任务、天猫任务、京东任务、阿里任务、拼多多任务、收藏任务、流量任务、快递单生成与查询系统、信用评

下载
  1. 检查存在性(EXISTS): 当你需要检查某个条件是否满足“存在”时,相关子查询结合EXISTS是一个非常直观的写法。例如,找出所有至少有一笔订单的客户,或者找出所有有员工的部门。这种情况下,EXISTS通常比IN更高效,因为它在找到第一个匹配项时就会停止扫描。
  2. “每组最高/最低”类问题(但通常有更好替代): 比如找出每个部门工资最高的员工。虽然可以用相关子查询实现,但现代SQL(如MySQL 8+的窗口函数)通常有更优的解决方案。
  3. 复杂的数据验证或过滤: 当子查询的过滤逻辑必须紧密依赖于外部查询的每一行数据时,相关子查询可能是最直接的表达方式。

什么时候应该避免(并考虑替代方案):

  1. 性能敏感的场景: 这是最主要的考量。由于相关子查询会为外部查询的每一行执行一次,如果外部查询结果集很大,或者子查询本身很复杂,性能会急剧下降。
  2. 当存在更优的替代方案时:
    • JOIN 大多数可以用相关子查询实现的逻辑,特别是EXISTSNOT EXISTS,都可以通过INNER JOINLEFT JOINNOT IN(配合LEFT JOIN ... IS NULL)来改写。JOIN通常能利用索引,并且优化器对其有更好的优化策略。
    • 派生表(Derived Table): 对于非相关子查询,如果它返回一个结果集,可以将其作为一个派生表(即在FROM子句中的子查询)来处理,这能让优化器更好地处理它。
    • IN操作符: 当子查询返回一个列的列表,并且外部查询需要判断某个值是否在这个列表中时,IN操作符是首选。但要注意,如果IN子查询是相关的,性能问题依旧存在。

我的经验是,能用JOIN解决的问题,尽量用JOIN。如果必须用子查询,优先考虑非相关子查询。相关子查询是最后的选择,或者在代码可读性极高且性能影响可接受时才使用。

性能考量:如何优化包含子查询的SQL语句?

优化包含子查询的SQL语句,特别是相关子查询,是数据库性能调优中一个常见且重要的环节。

  1. 使用 EXPLAIN 分析执行计划: 这是任何SQL优化工作的起点。EXPLAIN可以清晰地告诉你MySQL是如何执行你的查询的,包括子查询是否被优化器改写、是否是DEPENDENT SUBQUERY(相关子查询的标识)、扫描了多少行、是否使用了索引等。通过分析EXPLAIN的输出,你能找出性能瓶颈所在。

  2. 将相关子查询改写为 JOIN 操作: 这是最常见的也是最有效的优化手段之一。

    • EXISTS改写为 INNER JOIN 如果子查询是用来检查是否存在匹配项,并且外部查询需要返回所有匹配的行,通常可以改写为INNER JOIN

      -- 原始相关子查询
      SELECT d.department_name
      FROM departments d
      WHERE EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.department_id);
      
      -- 优化为 JOIN
      SELECT DISTINCT d.department_name
      FROM departments d
      INNER JOIN employees e ON d.department_id = e.department_id;
    • NOT EXISTS改写为 LEFT JOIN ... IS NULL 如果子查询是用来检查不存在匹配项,通常可以改写为LEFT JOIN后判断关联列是否为NULL

      -- 原始相关子查询
      SELECT d.department_name
      FROM departments d
      WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.department_id);
      
      -- 优化为 JOIN
      SELECT d.department_name
      FROM departments d
      LEFT JOIN employees e ON d.department_id = e.department_id
      WHERE e.department_id IS NULL;
  3. 确保子查询内部和外部查询的关联列有索引: 无论子查询是相关还是非相关,如果其内部的WHERE条件或JOIN条件涉及到的列没有索引,都会导致全表扫描,极大降低性能。特别是对于相关子查询,每次执行子查询时都会用到这些列,索引的作用更为关键。

  4. 利用派生表(Derived Tables)优化非相关子查询: 对于非相关子查询,如果它返回一个结果集,可以将其放在FROM子句中作为派生表。MySQL优化器通常能更好地处理派生表,甚至可能对其进行物化(将子查询结果存储在临时表中),从而避免重复计算。

  5. 减少子查询返回的数据量: 无论子查询是相关还是非相关,如果它返回的数据量过大,都会增加内存和CPU的开销。尝试在子查询内部就进行更严格的过滤,只返回必要的数据。

  6. 考虑使用 WITH 子句(CTE - Common Table Expressions): MySQL 8.0+ 支持 WITH 子句,它可以让复杂的查询逻辑更清晰,也为优化器提供了更多优化机会。虽然 WITH 本身不直接优化性能,但它能帮助你更好地组织和重构查询,从而更容易发现和应用上述优化策略。

优化子查询是一个迭代的过程,需要结合EXPLAIN的输出和实际的业务场景来不断尝试和调整。很多时候,从逻辑上等价的多种写法中,选择一个对数据库优化器最友好的方式,就能带来显著的性能提升。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

680

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

320

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

347

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1095

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

357

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

676

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

574

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

416

2024.04.29

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 1.8万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 799人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号