子查询优化
查询优化
连接优化
条件化简
- 移除不必要的括号。
- 常量传递,例如条件中有
a = 5 and b > a会变为a = 5 and b > 5 - 移除没必要的条件,如移除永远为True和False的条件。
- 表达式的计算,将能够计算的就计算出来。
Having和Where子句的合并,如果没有出现聚合函数、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
)派生表的处理类似,也是先尝试连接,不行则物化查询。