文档首页
MySQL 8.4 参考手册
相关文档 下载此手册
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 参考手册  /  ...  /  使用合并或物化优化派生表、视图引用和通用表表达式

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 子句。

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

  • 可以使用 MERGENO_MERGE 优化器提示。假设没有其他规则阻止合并,它们适用。请参见 第 10.9.3 节,“优化器提示”

  • 类似地,您可以使用 derived_merge 标志,它是 optimizer_switch 系统变量的标志。请参见 第 10.9.2 节,“可切换优化”。默认情况下,该标志处于启用状态以允许合并。禁用该标志会阻止合并并避免出现 ER_UPDATE_TABLE_USED 错误。

    derived_merge 标志也适用于不包含 ALGORITHM 子句的视图。因此,如果针对使用与子查询等效表达式的视图引用出现了 ER_UPDATE_TABLE_USED 错误,则在视图定义中添加 ALGORITHM=TEMPTABLE 会阻止合并并优先于 derived_merge 值。

  • 可以通过在子查询中使用任何阻止合并的结构来禁用合并,尽管这些结构对物化的影响并不那么明确。阻止合并的结构对于派生表、通用表表达式和视图引用都是相同的

    • 聚合函数或窗口函数(SUM()MIN()MAX()COUNT() 等)

    • DISTINCT

    • GROUP BY

    • HAVING

    • LIMIT

    • UNIONUNION ALL

    • 选择列表中的子查询

    • 对用户变量的赋值

    • 仅引用字面值(在这种情况下,没有基础表)

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

  • 优化器会将派生表的物化推迟到查询执行期间需要其内容时。这将提高性能,因为延迟物化可能会导致根本不需要进行物化。考虑一个将派生表的结果与另一个表进行联接的查询:如果优化器先处理另一个表,并发现它没有返回任何行,则联接无需进一步执行,并且优化器可以完全跳过派生表的物化。

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

考虑以下 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;

如果这样做可以实现对 ref 访问,以便获得最低成本执行计划,优化器将在 derived_t2 的列 f1 上构建索引。添加索引后,优化器可以像对待具有索引的常规表一样对待物化的派生表,并且它可以从生成的索引中获得类似的益处。索引创建的开销与没有索引的查询执行成本相比可以忽略不计。如果 ref 访问会导致比其他一些访问方法更高的成本,则优化器不会创建任何索引,也不会损失任何东西。

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

对派生表的物化来说正确的,对公共表表达式 (CTE) 来说也是正确的。此外,以下注意事项专门针对 CTE。

如果查询对 CTE 进行物化,则会为查询物化一次,即使查询多次引用它也是如此。

递归 CTE 总是会被物化。

如果对 CTE 进行物化,优化器会自动添加相关的索引,如果它估计索引可以加快顶级语句对 CTE 的访问速度。这类似于派生表的自动索引,不同之处在于,如果多次引用 CTE,优化器可能会创建多个索引,以通过最合适的方式加快每个引用的访问速度。

可以在 CTE 上应用 MERGENO_MERGE 优化器提示。顶级语句中的每个 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 子句不会影响视图定义中 SELECT 语句之前的任何 WITH 子句的物化。考虑以下语句

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

ALGORITHM 值仅影响 SELECT 的物化,而不影响 WITH 子句。

如前所述,如果对 CTE 进行物化,则会物化一次,即使多次引用它也是如此。为了指示一次性物化,优化器跟踪输出包含 creating_tmp_table 的一次出现以及一个或多个 reusing_tmp_table 的出现。

CTE 类似于派生表,对于派生表,materialized_from_subquery 节点位于引用之后。这对于多次引用的 CTE 来说是正确的,因此不会重复 materialized_from_subquery 节点(这会让人以为子查询执行了多次,并产生不必要的冗长输出)。只有一个对 CTE 的引用具有带有其子查询计划描述的完整 materialized_from_subquery 节点。其他引用具有简化的 materialized_from_subquery 节点。相同的概念适用于 TRADITIONAL 格式的 EXPLAIN 输出:不会显示其他引用的子查询。