2.2 Optimizing Subqueries with Materialization 通过物化来优化子查询
优化器使用物化能够更有效的来处理子查询。物化通过将子查询结果作为一个临时表来加快查询执行速度,正常来说是在内存中的。mysql第一次需要子查询结果是,它物化结果到一张临时表中。在之后的任何地方需要该结果集,mysql会再次引用临时表。优化器也许会使用一个哈希索引来使得查询更快速代价更小。索引是唯一的,排除重复并使得表数据更少。
子查询物化如果可以的话会使用一个内存临时表,如果表太大则会落实为磁盘存储。具体请看8.4.4的在mysql中使用内部临时表。
如果物化不可用,优化器有时会重写一个不正确的子查询作为一个正确的子查询。例如,下面的IN子查询是不正确的(where_condition涉及的列仅仅来自表t2完全没有在t1中):
SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);
优化器也许会重写上述查询为一个EXISTS正确的子查询
SELECT * FROM t1
WHERE EXISTS (SELECT t2.b FROM t2 WHERE where_condition AND t1.a=t2.b);
子查询物化使用临时表避免如此的重写并且使得子查询只执行一次而不是每一行都执行一次对于外部查询。
对于在mysql中使用子查询物化,系统变量optimizer_switch标志的materialization标记必须是可用的。(具体请看8.9.3优化的开关)materialization标志可用,物化可能应用在子查询出现的任何地方(在select列表,where,on,group by,having,或者是order by),对于下面这样的情况都会使用:
1)谓词有这样的形式,当没有外部表达式oe_i或者是内部表达式ie_i是没有值时(null),N是1或者是更大。
(oe_1, oe_2, ..., oe_N) [NOT] IN (SELECT ie_1, i_2, ..., ie_N ...)
2)谓词有这样的形式,当外部表达式oe和内部表达式ie都是单一个的话,表达式可以为null.
oe [NOT] IN (SELECT ie ...)
3) 谓词是IN或者是NOT IN并且结果集中关于UNKNOWN(NULL)同样意味着是False的结果。
下面的例子说明了关于UNKNOW和FALSE有怎样的要求才会形成等价影响对于子查询是否使用物化。假设where_condition涉及到的列只在t2表中不在t1表中所以子查询不会正确。
查询被物化:
SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);
这里,它没有关心IN谓词是否返回了UNKNOWN或者是FALSE.无所谓,从t1中的行并没有保护在查询结果中。
一个例子关于子查询物化没有使用在如下查询中,其中t2.b是一个可为null的列:
SELECT * FROM t1
WHERE (t1.a,t1.b) NOT IN (SELECT t2.a,t2.b FROM t2
                          WHERE where_condition);
下面是关于使用子查询物化的限制:
>内部表达式和外部表达式的类型必须匹配。例如,优化器也许能够使用物化,如果两个表达式都是integer或者是都是decimal,但是不能使用如果其中一个是integer而另一个是decimal.
>内部表达式的类型不能是BLOB。
使用Explain信息一个查询提供的一些信息关于是否优化器使用了子查询物化。对比查询执行没有使用物化,select_type的值可能由DEPENDENT SUBQUERY到SUBQUERY。这说明,对于一个子查询其中的数据对于外部行的每一行数据都执行一次,物化能够使得子查询只执行一次。另外,对于扩展的EXPLAIN输出,文本被展示在SHOW WARNINGS后面的包含了materialize和materialized-subquery。
到此关于物化优化子查询的说明就结束了,接下来我们要说的是2.3Optimizing Derived Tables and View References 优化派生表和试图引用。