子查询优化

查询优化

连接优化

条件化简

  • 移除不必要的括号。
  • 常量传递,例如条件中有a = 5 and b > a会变为a = 5 and b > 5
  • 移除没必要的条件,如移除永远为True和False的条件。
  • 表达式的计算,将能够计算的就计算出来。
  • HavingWhere子句的合并,如果没有出现聚合函数、GROUP BY等,Having就等同于where。
  • 常量表检测:如果查询条件为唯一索引等值匹配,或者表中不存在或者只存在一条记录,认为是常量表。

外连接消除

由于内连接的驱动表和被驱动表可以互相转换,可以进行优化。

但左(外)连接和右(外)连接的驱动表是固定的,这就导致无法优化连接顺序,因此考虑将外连接转化为内连接,然后进行内连接优化。

被驱动表的列不为NULL,称为空值拒绝reject NULL。此时,可以保证两表都符合连接条件,因此可以转化为内连接。

子查询优化

标量子查询、行子查询

由于只需要查询一个记录,因此,这种查询就如预期那样,对于驱动表的每一条记录都查询一次被驱动表。

IN子查询

物化表

如果表中数据过多,子查询查询到的结果太多,内存可能放不下,提出来物化表的概念,即将子查询结果集写入一个临时表。此外,因为IN只是判断是否存在,还对临时表的记录进行去重。

临时表的引擎可以是MEMORY,或者InnoDB,取决于表记录量。

转化为连接

如果外表与物化表可以转化为连接,则按连接处理,对连接进行分析优化。比如查询条件为唯一索引的等值查询,就可以转化为与物化表的内连接。

半连接

由于物化存在一定的开销,考虑直接和子表进行连接,如半连接。

s1和s2半连接的意思是,对于s1的某条记录来说,我们只关心在s2表中是否存在对应的记录,而不关心是一条还是多条。

半连接的方式有很多,例如,表上拉Table Pullout、重复值消除Duplicate Weedout、松散扫描Loose Scan、。

表上拉

当子查询的查询列表只有主键或者唯一索引时,可以把子查询的表上拉到外层的FROM子句中,并把搜索条件合并到外层查询的搜索条件中。如

SELECT * FORM s1
WHERE key2 IN (
	SELECT key2
    FROM s2
    WHERE key3 = 'a'
)

即可表上拉为

SELECT s1.* FROM s1 INNER JOIN s2 ON s1.key2 = s2.key2 WHERE s2.key3 = 'a'

之所以要求主键或者唯一索引,是因为不要求就会导致查询出多条相同记录。根据这点,就有了重复值消除的方式。

重复值消除

按照表上拉的方式连接,但由于不是唯一索引,可能重复,因此对结果进行去重。

总结

如果符合转化为半连接的条件,查询优化器会优先转化为半连接,然后根据预估成本从以下5种办连接的策略中选择执行

  • Table pullout
  • Duplicate Weed out
  • Loose Scan
  • Semi-join Materialization
  • First Match execution

如果不符合转化为半连接的条件,则会根据预估成本选择如下两种方式执行。

  • 先将子查询物化,再执行查询。
  • 执行IN到EXISTS的转换。

把子查询外在外成查询的FROM子句,这个子查询相当于派生表,如

SELECT * FROM (
 	SELECT * FROM tab where id > 1
)

派生表的处理类似,也是先尝试连接,不行则物化查询。


子查询优化
https://messenger1th.github.io/2024/07/24/MySQL/子查询优化/
作者
Epoch
发布于
2024年7月24日
许可协议