Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  Optimizing Derived Tables, View References, and Common Table Expressions with Merging or Materialization

10.2.2.4 派生表、视图引用和公共表表达式的优化,使用合并或物化

优化器可以使用两种策略处理派生表引用(这也适用于视图引用和公共表表达式):

  • 将派生表合并到外部查询块中

  • 将派生表物化到内部临时表中

示例 1:

SELECT * FROM (SELECT * FROM t1) AS derived_t1;

使用派生表 derived_t1 的合并,该查询的执行方式类似于:

SELECT * FROM t1;

示例 2:

SELECT *
  FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2 ON t1.f2=derived_t2.f1
  WHERE t1.f1 > 0;

使用派生表 derived_t2 的合并,该查询的执行方式类似于:

SELECT t1.*, t2.f1
  FROM t1 JOIN t2 ON t1.f2=t2.f1
  WHERE t1.f1 > 0;

使用物化,derived_t1derived_t2 分别作为其各自查询中的独立表。

优化器以相同的方式处理派生表、视图引用和公共表表达式:它尽可能避免不必要的物化,从而启用将条件从外部查询推送到派生表中,并生成更高效的执行计划。(例如,参见 第 10.2.2.2 节,“使用物化优化子查询”。)

如果合并将导致外部查询块引用超过 61 个基表,优化器将选择物化。

优化器将派生表或视图引用的 ORDER BY 子句传播到外部查询块,如果满足以下所有条件:

  • 外部查询不进行分组或聚合。

  • 外部查询不指定 DISTINCTHAVINGORDER BY

  • 外部查询的 FROM 子句中只有该派生表或视图引用。

否则,优化器将忽略 ORDER BY 子句。

以下方法可用于影响优化器是否尝试将派生表、视图引用和公共表表达式合并到外部查询块中:

如果优化器选择物化策略而不是合并派生表,它将按照以下方式处理查询:

  • 优化器将推迟派生表的物化,直到查询执行过程中需要其内容。这可以提高性能,因为延迟物化可能会导致不需要物化。考虑一个查询,它将派生表的结果与另一个表连接:如果优化器首先处理该其他表并发现它不返回任何行,那么连接不需要进一步执行,优化器可以完全跳过物化派生表。

  • 在查询执行期间,优化器可能会向派生表添加索引,以加速从中检索行。

考虑以下 EXPLAIN 语句,对于包含派生表的 SELECT 查询:

EXPLAIN SELECT * FROM (SELECT * FROM t1) AS derived_t1;

优化器通过延迟派生表的物化直到结果在 SELECT 执行期间需要时,避免了派生表的物化。在这种情况下,因为查询发生在 EXPLAIN 语句中,所以结果从不需要。

即使对于执行的查询,延迟派生表的物化也可能使优化器完全避免物化。当这种情况发生时,查询执行速度将加快,因为不需要执行物化所需的时间。考虑以下查询,它将派生表的结果与另一个表连接:

SELECT *
  FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2
          ON t1.f2=derived_t2.f1
  WHERE t1.f1 > 0;

如果优化器首先处理 t1,并且 WHERE 子句产生空结果,那么连接必然为空,派生表不需要物化。

对于需要物化的派生表,优化器可能会向物化的表添加索引,以加速对其的访问。如果这样的索引使得 ref 访问变得可能,它可以大大减少查询执行期间读取的数据量。考虑以下查询:

SELECT *
 FROM t1 JOIN (SELECT DISTINCT f1 FROM t2) AS derived_t2
         ON t1.f1=derived_t2.f1;

优化器将在 derived_t2 上构建一个索引,以便在最低成本的执行计划中使用 ref 访问。如果添加索引,优化器可以将物化的派生表视为一个普通的索引表,并从生成的索引中获益。索引创建的开销与查询执行的成本相比可以忽略。如果 ref 访问将导致更高的成本,那么优化器将不创建索引,也不会损失什么。

对于优化器跟踪输出,合并的派生表或视图引用不会显示为节点。只有其基础表在顶级查询计划中出现。

派生表物化的真理也适用于公共表表达式(CTEs)。此外,以下考虑也适用于 CTEs。

如果 CTE 被查询物化,它将被物化一次,即使查询引用它多次。

递归 CTE 总是被物化。

如果 CTE 被物化,优化器将自动添加相关索引,以便加速顶级语句对 CTE 的访问。这类似于派生表的自动索引,除了如果 CTE 被多次引用,优化器可能创建多个索引,以便加速每个引用的访问方式。

可以将 MERGENO_MERGE 优化器提示应用于 CTEs。每个 CTE 引用在顶级语句中可以有其自己的提示,允许 CTE 引用被选择性地合并或物化。以下语句使用提示来指示 cte1 应该被合并,而 cte2 应该被物化:

WITH
  cte1 AS (SELECT a, b FROM table1),
  cte2 AS (SELECT c, d FROM table2)
SELECT /*+ MERGE(cte1) NO_MERGE(cte2) */ cte1.b, cte2.d
FROM cte1 JOIN cte2
WHERE cte1.a = cte2.c;

对于 CREATE VIEWALGORITHM 子句不影响任何 WITH 子句在视图定义中的物化。考虑以下语句:

CREATE ALGORITHM={TEMPTABLE|MERGE} VIEW v1 AS WITH ... SELECT ...

ALGORITHM 值仅影响视图定义中的 SELECT 的物化,而不是 WITH 子句。

如前所述,如果 CTE 被物化,它将被物化一次,即使被多次引用。为了表明一次物化,优化器跟踪输出将包含一个 creating_tmp_table 及一个或多个 reusing_tmp_table

CTEs 与派生表相似,对于派生表,materialized_from_subquery 节点将跟随引用。这也适用于 CTE,即使它被多次引用,也不会出现 materialized_from_subquery 节点的重复(这将给人以子查询被多次执行的印象,并产生不必要的冗长输出)。只有一个 CTE 引用具有完整的 materialized_from_subquery 节点,描述其子查询计划。其他引用具有简化的 materialized_from_subquery 节点。同样的想法也适用于 EXPLAIN 输出的传统格式:其他引用的子查询不显示。