【MySQL】基于规则的优化(内含子查询优化;派生表;物化表;半连接;标量子查询;行子查询)

本文主要是介绍【MySQL】基于规则的优化(内含子查询优化;派生表;物化表;半连接;标量子查询;行子查询),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概念

  • 常量表:下述两种查询方式查询的表:

    • 类型1:查询的表中一条记录都没有,或者只有一条记录

    • 类型2:使用主键等值匹配或者唯一二级索引列等值匹配作为搜索条件来查询某个表

  • 派生表:放在FROM子句后面的子查询称为派生表。

  • 物化:将子查询结果集中的记录保存到临时表的过程。

  • 物化表存储子查询结果集的临时表

    • 基于内存的物化表哈希索引

    • 基于磁盘的物化表B+树索引

    • 正因为物化表中的记录都建立了索引,通过索引来判断某个操作数是否存在子查询结果集中时,速度会变得非常快,从而提升了子查询语句的性能。

第14章 基于规则的优化(内含子查询优化)

14.1 条件化简

  1. 移除不必要的括号

  2. 常量传递

  3. 移除没用的条件

  4. 表达式计算

  5. HAVING子句和WHERE子句合并

    • 如果查询语句中没有出现聚集函数及GROUP BY子句,则可合并。
  6. 常量表检测

    • 查询优化器在分析一个查询语句时,首先执行常量表查询,然后把查询中涉及该表的条件全部替换为常数,最后再分析其余表的查询成本。

14.2 外连接消除

  1. 外连接 和 内连接 本质区别:

    • 对于外连接的驱动表的记录来说,如果无法在被驱动表中找到匹配ON子句中过滤条件的记录,那么该驱动表记录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用NULL值填充;

    • 而内连接的驱动表的记录如果无法在被驱动表中找到匹配ON子句中过滤条件的记录,那么该驱动表记录会被舍弃。

  2. 空值拒绝(reject-NULL):在外连接查询中,指定的WHERE子句中包含被驱动表中的列不为NULL值条件称为空值拒绝。

    • 即:显式指定被驱动表的某个列符合 IS NOT NULL,或者,隐含点,直接指定被驱动表的某个列符合指定的值,此时就能将 外连接 转为 内连接。

      select * from t1 left join t2 on t1.m1=t2.m2 where t2.n2 is not NULL;
      # 等价于
      select * from t1 inner join t2 on t1.m1 = t2.m2;
      
      select * from t1 left join t2 on t1.m1=t2.m2 where t2.n2 = 2;
      # 直接指定'被驱动表的某个列'符合指定的值,此时就能将 外连接 转为 内连接。
      
  3. 在被驱动表的WHERE子句符合空值拒绝的条件后,外连接和内连接可以相互转换。

  4. 好处:优化器通过评估表的不同连接顺序的成本,选出成本最低的连接顺序执行查询。

14.3 子查询优化

14.3.1 子查询语法

  1. 子查询可以在一个外层查询的各种位置出现。

    • 在SELECT子句中

    • 在FROM子句中

    • 在WHERE 或 ON子句中

    • 在 ORDER BY 子句 和 GROUP BY子句中,虽然语法支持,但无意义。

  2. 按照返回结果集区分子查询:

    • 标量子查询:只返回一个单一值的子查询。

    • 行子查询:返回一条记录的子查询(一条记录可能包含多个列)。

    • 列子查询:查出一个列的数据(这个列可能包含多条记录)。

    • 表子查询:子查询的结果既包含很多条记录,又包含很多个列。

  3. 按与外层查询的关系来区分子查询:

    • 不相关子查询:子查询可以单独运行出结构,而不依赖于外城查询的值。

    • 相关子查询:子查询的执行需要依赖于外层查询的值。

  4. 子查询在布尔表达式中的使用

    1. 使用 =、>、<、>=、 <=、 <>、 !=、 <=> 作为布尔表达式的操作符

      • 注意:这里的子查询只能是标量子查询or行子查询,即,子查询的结果只能返回一个单一的值or只能是一条记录。
       # 标量子查询select * from t1 where m1 < (select MIN(m2 from t2); # 行子查询select * from t1 where (m1,n1)=(select m2,n2 from t2 limit 1);
      
    2. [NOT] IN / ANY / SOME / ALL 子查询

    3. EXISTS子查询

  5. 子查询语法注意事项

    • 子查询必须用小括号括起来。

    • SELECT子句中的子查询必须是标量子查询(注意,不是from/where等子句)。

        # slect 子句中的子查询  是 标量子查询select (select m1, n1 from t1); # where/from子句中的子查询 也可以是 行子查询```
    • 想要得到标量子查询or行子查询,但又不能保证子查询的结果集只有一条记录时,应该使用 LIMIT 1 语句来限制记录数量。

    • 对于[NOT] IN / ANY / SOME / ALL 子查询来说,子查询中不允许有LIMIT语句。

      • 注意:对于[NOT] IN / ANY / SOME / ALL 子查询来说,在子查询中使用 ORDER BY子句、DISTINCT子句,以及没有聚集函数和 HAVING子句的GROUP BY 子句是无意义的。因为,子查询的结果其实相当于是一个集合,集合里的值是否排序一点也不重要。比如:

        select * from t1 where m1 in (selct m2 from t2 order by m2);
        
      • 在没有聚集函数以及HAVING子句时,GROUP BY 子句就是个摆设。比如:

        select * from t1 where m1 in (selct m2 from t2 group by m2);
        
    • 不允许在一条语句中增删改某个表的记录时,同时还对该表进行子查询。

14.3.2 子查询在MySQL中是怎么执行的

1. 标量子查询、行子查询的执行方式
  1. 不相关标量/行子查询语句:独立执行外层查询和子查询(当作两个单表查询)。

  2. 相关标量/行子查询语句:例1,

    # 例1
    select * from s1 where key = (select common_field from s2 where s1.key3 = s2.key3 limit 1);
    

    执行方式:

    • 步骤1:先从外层查询中获取一条记录。在例1中,先从s1表中获取一条记录。

    • 步骤2:然后从这条记录中找出子查询中涉及的值。 在例1中,从s1表中获取的那条记录中找出s1.key3列的值,然后执行子查询。

    • 步骤3:最后根据子查询的查询结果来检测外层查询WHERE子句的条件是否成立。如果成立,就把外层查询的那条记录加入到结果集,否则就丢弃。

    • 步骤4:跳到步骤1,直到外层查询中获取不到记录为止。

2. IN子查询优化
  1. 物化表的提出。

    • 物化:将子查询结果集中的记录保存到临时表的过程。

    • 物化表存储子查询结果集的临时表

      • 基于内存的物化表哈希索引

      • 基于磁盘的物化表B+树索引

      • 正因为物化表中的记录都建立了索引,通过索引来判断某个操作数是否存在子查询结果集中时,速度会变得非常快,从而提升了子查询语句的性能。

  2. 物化表转连接

    # 查询1
    select * from s1
    where key1 in (slecet common_field from s2 where key3 = 'a);# 当把子查询物化之后,假设子查询物化表的名称为 materialized_table, 
    # 该物化表存储的子查询结果集的列为 m_val。# 则相当于表s1与子查询物化表 materialized_table进行内连接。
    # 即,查询1 等价于 查询2# 查询2
    select s1.* from s1 
    inner join materialized_table on key1 = m_val;
    
    • 转换为内连接之后,查询优化器可以评估不同连接顺序需要的成本是多少,然后从中选取成本最低的那种方式执行查询。

    • 分析查询2(表s1和物化表materialized_table内连接)的成本构成:

      1. 如果使用 s1表作为驱动表,总查询成本如下:
      • 物化子查询时需要的成本;

      • 扫描s1表时的成本;

      • s1表中的记录数量 x 通过条件 m_val=xxx 对 materialized_table表进行单表访问的成本(由于物化表中的记录时不重复的,并且为物化表中的列建立了索引,所以此步骤非常快)。

      1. 如果使用 materialized_table 表作为驱动表,总查询成本如下:
      • 物化子查询时需要的成本;

      • 扫描物化表时的成本;

      • 物化表中的记录数量 x 通过条件 key1=xxx 对 s1 表进行单表访问的成本(由于在key1列上建立了索引,所以此步骤非常快)。

  3. 将子查询转换为半连接

    将子查询物化之后再执行查询,会有建立临时表的成本,那,能不能不进行物化操作,直接把子查询转换为连接呢?

    # 查询1
    select * from s1
    where key1 in (slecet common_field from s2 where key3 = 'a);
    
    • 提出新概念——半连接

    • 注意:半连接,是为了 IN子查询优化 的。

    • 将 s1表和s2表进行半连接的意思是:

      • 对于s1表中的某条记录来说,我们只关心在s2表中是否存在与之匹配的记录,而不关心具体由多少条记录与之匹配,最终的结果集中只保留s1表的记录。

      • # 半连接,只是在MySQL内部采用的一种执行子查询的方式,
        # MySQL没有提供面向用户的半连接语法。
        # 所以此语句不能放在黑框框中运行。
        select s1.* from s1 SEMI JOIN s2on s1.key1 = s2.common_fieldwhere key3 = 'a';
        
    • 实现 半连接 方法(策略):

      • Table pullout (子查询中的表上拉)

      • Duplicate Weedout (重复值消除)

      • LooseScan(松散扫描)

      • Semi-join Materialization(半连接物化)

      • FirstMatch(首次匹配)

    • 半连接的适用条件 (只有符合下述条件的子查询才可以转换为半连接)

      • 该子查询必须是与IN操作符组成的布尔表达式,并且在外城查询的WHERE 或者ON子句中出现;

      • 外层查询也可以由其他的搜索条件,只不过必须使用AND操作符与IN子查询的搜索条件连接起来;

      • 该子查询必须是一个单一的查询,不能是由UNION连接起来的若干个查询;

      • 该子查询不能包含GROUP BY、HAVING语句或者聚集函数。

    • 不适用于半连接的情况

      • 在外层查询的WHERE子句中,存在其他搜索条件使用OR操作符与IN子查询组成的布尔表达式连接起来的情况。

      • 使用 NOT IN 而不是 IN 的情况。

      • 位于 SELECT 子句中的 IN 子查询的情况。

      • 子查询中包含 GROUP BY 、HAVING 或者 聚集函数的情况。

      • 子查询中包含 UNION 的情况。

      • 对于不相关的子查询,可以尝试把它们物化之后再参与查询。

      • 无论子查询是相关的还是不相关的,都可以把 IN 子查询尝试转为 EXISTS 子查询。

    • 小结

      • 如果 IN 子查询符合转换为半连接的条件,查询优化器会优先把该子查询转换为半连接,再考虑下面5种执行半连接的策略中哪个的成本最低,最后从中选择成本最低的执行策略来执行子查询。

        • Table pullout (子查询中的表上拉)

        • Duplicate Weedout (重复值消除)

        • LooseScan(松散扫描)

        • Semi-join Materialization(半连接物化)

        • FirstMatch(首次匹配)

      • 如果 IN 子查询不符合转换为半连接的条件,那么查询优化器会从下面两种策略中找出一种成本更低的方式来执行子查询:

        • 先将子查询物化,再执行查询;

        • 执行 IN 到 EXISTS 的转换。

3. ANY / ALL 子查询优化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qxlXmvU4-1683462178280)(C:\Users\YUE\AppData\Roaming\marktext\images\2023-05-07-19-57-24-image.png)]

4. [NOT] EXISTS 子查询的执行

5. 对于派生表的优化
  1. 派生表:放在FROM子句后面的子查询称为派生表。

  2. 对于含有派生表的查询,MySQL提供了两种执行策略。

  • 把派生表物化

    • 思想:将派生表的结果集写到一个内部的临时表(物化表)中,把此物化表当作普通表一样来参与查询。

    • 延迟物化策略:在查询中真正使用到派生表时,才会去尝试物化派生表,而不是在执行查询之前就先把派生表物化。

  • 将派生表和外层查询合并(即,将查询 重写为 没有派生表的形式)

——读书笔记,摘自《MySQL是怎样运行的》

这篇关于【MySQL】基于规则的优化(内含子查询优化;派生表;物化表;半连接;标量子查询;行子查询)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/377673

相关文章

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

SQL Server安装时候没有中文选项的解决方法

《SQLServer安装时候没有中文选项的解决方法》用户安装SQLServer时界面全英文,无中文选项,通过修改安装设置中的国家或地区为中文中国,重启安装程序后界面恢复中文,解决了问题,对SQLSe... 你是不是在安装SQL Server时候发现安装界面和别人不同,并且无论如何都没有中文选项?这个问题也

2025版mysql8.0.41 winx64 手动安装详细教程

《2025版mysql8.0.41winx64手动安装详细教程》本文指导Windows系统下MySQL安装配置,包含解压、设置环境变量、my.ini配置、初始化密码获取、服务安装与手动启动等步骤,... 目录一、下载安装包二、配置环境变量三、安装配置四、启动 mysql 服务,修改密码一、下载安装包安装地

MySQL CTE (Common Table Expressions)示例全解析

《MySQLCTE(CommonTableExpressions)示例全解析》MySQL8.0引入CTE,支持递归查询,可创建临时命名结果集,提升复杂查询的可读性与维护性,适用于层次结构数据处... 目录基本语法CTE 主要特点非递归 CTE简单 CTE 示例多 CTE 示例递归 CTE基本递归 CTE 结

MySQL多实例管理如何在一台主机上运行多个mysql

《MySQL多实例管理如何在一台主机上运行多个mysql》文章详解了在Linux主机上通过二进制方式安装MySQL多实例的步骤,涵盖端口配置、数据目录准备、初始化与启动流程,以及排错方法,适用于构建读... 目录一、什么是mysql多实例二、二进制方式安装MySQL1.获取二进制代码包2.安装基础依赖3.清

详解MySQL中JSON数据类型用法及与传统JSON字符串对比

《详解MySQL中JSON数据类型用法及与传统JSON字符串对比》MySQL从5.7版本开始引入了JSON数据类型,专门用于存储JSON格式的数据,本文将为大家简单介绍一下MySQL中JSON数据类型... 目录前言基本用法jsON数据类型 vs 传统JSON字符串1. 存储方式2. 查询方式对比3. 索引

小白也能轻松上手! 路由器设置优化指南

《小白也能轻松上手!路由器设置优化指南》在日常生活中,我们常常会遇到WiFi网速慢的问题,这主要受到三个方面的影响,首要原因是WiFi产品的配置优化不合理,其次是硬件性能的不足,以及宽带线路本身的质... 在数字化时代,网络已成为生活必需品,追剧、游戏、办公、学习都离不开稳定高速的网络。但很多人面对新路由器

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

MySQL 8 中的一个强大功能 JSON_TABLE示例详解

《MySQL8中的一个强大功能JSON_TABLE示例详解》JSON_TABLE是MySQL8中引入的一个强大功能,它允许用户将JSON数据转换为关系表格式,从而可以更方便地在SQL查询中处理J... 目录基本语法示例示例查询解释应用场景不适用场景1. ‌jsON 数据结构过于复杂或动态变化‌2. ‌性能要