#### 8.2.2.4 优化派生表、视图引用和公用表表达式

合并或物化

[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)

优化器可以使用两种策略处理派生表引用（这也适用于视图引用和公用表表达式）：

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

-   将派生表具体化为内部临时表

    示例 1：


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

随着派生表的合并`衍生_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;
```

随着派生表的合并`衍生_t2`，该查询的执行类似于：

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

随着物化，`衍生_t1`和`衍生_t2`在各自的查询中，每个都被视为单独的表。

优化器以相同的方式处理派生表、视图引用和公用表表达式：它尽可能避免不必要的物化，从而将条件从外部查询下推到派生表，并产生更有效的执行计划。（例如，参见[第 8.2.2.2 节，“通过实现优化子查询”](subquery-materialization.html).)

如果合并将导致引用超过 61 个基表的外部查询块，则优化器将选择具体化。

优化器传播一个`订购方式`如果这些条件都为真，则派生表或视图中的子句对外部查询块的引用：

-   外部查询未分组或聚合。

-   外部查询未指定`清楚的`,`拥有`， 要么`订购方式`.

-   外部查询将此派生表或视图引用作为`从`条款。

    否则，优化器会忽略`订购方式`条款。

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

-   这[`合并`](optimizer-hints.html#optimizer-hints-table-level)和[`NO_MERGE`](optimizer-hints.html#optimizer-hints-table-level)可以使用优化器提示。它们适用假设没有其他规则阻止合并。看[第 8.9.3 节，“优化器提示”](optimizer-hints.html).

-   同样，您可以使用[`派生合并`](switchable-optimizations.html#optflag_derived-merge)国旗[`优化器开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量。看[第 8.9.2 节，“可切换的优化”](switchable-optimizations.html).默认情况下，启用该标志以允许合并。禁用该标志可防止合并并避免[`ER_UPDATE_TABLE_USED`](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_update_table_used)错误。

    这[`派生合并`](switchable-optimizations.html#optflag_derived-merge)标志也适用于不包含`算法`条款。因此，如果一个[`ER_UPDATE_TABLE_USED`](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_update_table_used)使用与子查询等效的表达式的视图引用发生错误，添加`算法=可取的`到视图定义防止合并并优先于[`派生合并`](switchable-optimizations.html#optflag_derived-merge)价值。

-   可以通过在子查询中使用任何阻止合并的结构来禁用合并，尽管这些结构对具体化的影响并不明确。对于派生表、公用表表达式和视图引用，阻止合并的构造是相同的：

     [](<>)[](<>)

    -   聚合函数或窗口函数 ([`和（）`](aggregate-functions.html#function_sum),[`最小（）`](aggregate-functions.html#function_min),[`最大限度（）`](aggregate-functions.html#function_max),[`数数（）`](aggregate-functions.html#function_count), 等等)

    -   `清楚的`

    -   `通过...分组`

    -   `拥有`

    -   `限制`

    -   [`联盟`](union.html)要么[`联合所有`](union.html)

    -   选择列表中的子查询

    -   分配给用户变量

    -   仅引用文字值（在这种情况下，没有基础表）

    如果优化器选择实现策略而不是合并派生表，它会按如下方式处理查询：

-   优化器推迟派生表实现，直到查询执行期间需要其内容。这提高了性能，因为延迟实现可能导致根本不必这样做。考虑一个将派生表的结果连接到另一个表的查询：如果优化器首先处理另一个表并发现它没有返回任何行，则不需要进一步执行连接，优化器可以完全跳过实现派生表。

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

    考虑以下[`解释`](explain.html)声明，对于一个[`选择`](select.html)包含派生表的查询：


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

优化器通过延迟派生表直到需要结果来避免实现派生表[`选择`](select.html)执行。在这种情况下，不会执行查询（因为它发生在[`解释`](explain.html)语句），因此从不需要结果。

即使对于执行的查询，派生表实现的延迟也可以使优化器完全避免实现。发生这种情况时，查询执行速度会比执行具体化所需的时间更快。考虑以下查询，它将派生表的结果连接到另一个表：

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

如果优化过程`t1`首先和`在哪里`子句产生空结果，连接必须为空，并且派生表不需要具体化。

对于派生表需要物化的情况，优化器可能会向物化表添加索引以加快对其的访问。如果这样的索引启用[`参考`](explain-output.html#jointype_ref)访问表，可以大大减少查询执行过程中读取的数据量。考虑以下查询：

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

优化器在列上构造索引`f1`从`衍生_t2`如果这样做可以使用[`参考`](explain-output.html#jointype_ref)访问成本最低的执行计划。添加索引后，优化器可以将物化派生表视为具有索引的常规表，并且它同样受益于生成的索引。与没有索引的查询执行成本相比，索引创建的开销可以忽略不计。如果[`参考`](explain-output.html#jointype_ref)访问将导致比其他访问方法更高的成本，优化器不会创建索引并且不会丢失任何内容。

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

派生表的具体化适用于公用表表达式 (CTE)。此外，以下注意事项特别适用于 CTE。

如果 CTE 由查询具体化，则即使查询多次引用它，它也会为查询具体化一次。

递归 CTE 总是物化的。

如果 CTE 被具体化，如果优化器估计索引可以加快顶层语句对 CTE 的访问，则它会自动添加相关索引。这类似于派生表的自动索引，只是如果 CTE 被多次引用，优化器可能会创建多个索引，以便以最合适的方式加速每个引用的访问。

这[`合并`](optimizer-hints.html#optimizer-hints-table-level)和[`NO_MERGE`](optimizer-hints.html#optimizer-hints-table-level)优化器提示可以应用于 CTE。顶级语句中的每个 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-view.html)不影响任何实现[`和`](with.html)前面的条款[`选择`](select.html)视图定义中的声明。考虑这个陈述：

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

这`算法`价值只影响物化[`选择`](select.html)，而不是[`和`](with.html)条款。

在 MySQL 8.0.16 之前，如果[`internal_tmp_disk_storage_engine=MYISAM`](server-system-variables.html#sysvar_internal_tmp_disk_storage_engine), 任何尝试使用磁盘临时表实现 CTE 时都会发生错误，因为对于 CTE，用于磁盘内部临时表的存储引擎不能`MyISAM`.从 MySQL 8.0.16 开始，这不再是问题，因为`临时表`现在总是使用`InnoDB`用于磁盘内部临时表。

如前所述，即使被多次引用，CTE 如果具体化，也会被具体化一次。为了指示一次性实现，优化器跟踪输出包含出现`正在创建_tmp_table`加上一次或多次出现`重用_tmp_table`.

CTE 类似于派生表，其`materialized_from_subquery`节点跟随参考。这对于被多次引用的 CTE 是正确的，因此没有重复`materialized_from_subquery`节点（这会给人一种子查询被执行多次的印象，并产生不必要的冗长输出）。只有一个对 CTE 的引用具有完整的`materialized_from_subquery`节点及其子查询计划的描述。其他参考有减少`materialized_from_subquery`节点。同样的想法适用于[`解释`](explain.html)输出在`传统的`格式：不显示其他引用的子查询。
