优化器使用物化来实现更有效的子查询处理。物化通过生成子查询结果作为临时表(通常在内存中)来加快查询执行速度。第一次 MySQL 需要子查询结果时,它会将该结果物化到一个临时表中。之后每次需要该结果时,MySQL 都会再次引用该临时表。优化器可能会使用哈希索引对该表进行索引,以使查找速度快且成本低。索引包含唯一值以消除重复并使表更小。
子查询物化在可能的情况下使用内存中的临时表,如果表变得太大,则回退到磁盘存储。请参阅第 10.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
标志必须启用。(请参阅第 10.9.2 节,“可切换优化”。)在启用 materialization
标志后,物化将应用于出现在任何地方(在 select 列表、WHERE
、ON
、GROUP BY
、HAVING
或 ORDER BY
中)的子查询谓词,以及属于以下任何用例的谓词
当没有外层表达式
oe_i
或内层表达式ie_i
是可为空时,谓词具有以下形式。N
为 1 或更大。(oe_1, oe_2, ..., oe_N) [NOT] IN (SELECT ie_1, i_2, ..., ie_N ...)
当存在单个外层表达式
oe
和内层表达式ie
时,谓词具有以下形式。表达式可以是可为空的。oe [NOT] IN (SELECT ie ...)
谓词为
IN
或NOT IN
且UNKNOWN
(NULL
) 的结果与FALSE
的结果具有相同的含义。
以下示例说明了 UNKNOWN
和 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
是可为空的列
SELECT * FROM t1
WHERE (t1.a,t1.b) NOT IN (SELECT t2.a,t2.b FROM t2
WHERE where_condition);
使用子查询物化有以下限制
内层表达式和外层表达式的类型必须匹配。例如,如果两个表达式都是整数或都是十进制数,优化器可能可以使用物化,但如果一个表达式是整数,另一个表达式是十进制数,则不能使用物化。
内层表达式不能是
BLOB
。
使用带有查询的 EXPLAIN
提供了优化器是否使用子查询物化的一些指示
与不使用物化的查询执行相比,
select_type
可能从DEPENDENT SUBQUERY
更改为SUBQUERY
。这表明,对于一个原本会为每个外部行执行一次的子查询,物化使子查询仅执行一次成为可能。对于扩展的
EXPLAIN
输出,以下SHOW WARNINGS
显示的文本包含materialize
和materialized-subquery
。
MySQL 还可以将子查询物化应用于使用 [NOT] IN
或 [NOT] EXISTS
子查询谓词的单表 UPDATE
或 DELETE
语句,前提是该语句不使用 ORDER BY
或 LIMIT
,并且子查询物化被优化器提示或 optimizer_switch
设置允许。