### 8.8.2 解释输出格式

这[`解释`](explain.html)语句提供有关 MySQL 如何执行语句的信息。[`解释`](explain.html)与[`选择`](select.html),[`删除`](delete.html),[`插入`](insert.html),[`代替`](replace.html)， 和[`更新`](update.html)陈述。

[`解释`](explain.html)为使用的每个表返回一行信息[`选择`](select.html)陈述。它按照 MySQL 在处理语句时读取它们的顺序列出输出中的表。这意味着 MySQL 从第一个表中读取一行，然后在第二个表中找到匹配的行，然后在第三个表中，依此类推。处理完所有表后，MySQL 会输出选定的列并通过表列表回溯，直到找到匹配行较多的表。从此表中读取下一行，并继续处理下一个表。

笔记

MySQL Workbench 具有 Visual Explain 功能，可提供[`解释`](explain.html)输出。看[教程：使用 Explain 提高查询性能](https://dev.mysql.com/doc/workbench/en/wb-tutorial-visual-explain-dbt3.html).

-   [解释输出列](explain-output.html#explain-output-columns)

-   [解释连接类型](explain-output.html#explain-join-types)

-   [解释额外信息](explain-output.html#explain-extra-information)

-   [EXPLAIN 输出解释](explain-output.html#explain-output-interpretation)

#### 解释输出列

本节描述由生成的输出列[`解释`](explain.html).后面的部分提供了有关[`类型`](explain-output.html#explain-join-types)和[`额外的`](explain-output.html#explain-extra-information)列。

每个输出行来自[`解释`](explain.html)提供有关一张表的信息。每行包含汇总的值[表 8.1，“解释输出列”](explain-output.html#explain-output-column-table)，并在下表中进行了更详细的描述。列名显示在表格的第一列；第二列提供了输出中显示的等效属性名称，当`格式=JSON`用来。

[](<>)

**表 8.1 解释输出列**

| 柱子 | JSON 名称 | 意义 |
| --- | ------- | --- |
| [`id`](explain-output.html#explain_id) | `select_id` | 这`选择`标识符 |
| [`选择类型`](explain-output.html#explain_select_type) | 没有 | 这`选择`类型 |
| [`桌子`](explain-output.html#explain_table) | `表名` | 输出行的表 |
| [`分区`](explain-output.html#explain_partitions) | `分区` | 匹配的分区 |
| [`类型`](explain-output.html#explain_type) | `访问类型` | 联接类型 |
| [`可能的键`](explain-output.html#explain_possible_keys) | `可能的键` | 可供选择的索引 |
| [`钥匙`](explain-output.html#explain_key) | `钥匙` | 实际选择的索引 |
| [`key_len`](explain-output.html#explain_key_len) | `密钥长度` | 所选密钥的长度 |
| [`参考`](explain-output.html#explain_ref) | `参考` | 与索引比较的列 |
| [`行`](explain-output.html#explain_rows) | `行` | 估计要检查的行数 |
| [`过滤`](explain-output.html#explain_filtered) | `过滤` | 按表条件过滤的行百分比 |
| [`额外的`](explain-output.html#explain_extra) | 没有 | 附加信息 |

笔记

JSON 属性是`空值`不以 JSON 格式显示`解释`输出。

-   [](<>) `id`（JSON 名称：`select_id`)

    这[`选择`](select.html)标识符。这是序号[`选择`](select.html)在查询中。该值可以是`空值`如果该行引用其他行的联合结果。在这种情况下，`桌子`列显示一个值，如`<工会*`米`*,*`ñ`*>`表示该行是指行的并集`id`的值*`米`*和*`ñ`*.

-   [](<>) `选择类型`（JSON 名称：无）

    的类型[`选择`](select.html)，可以是下表中显示的任何一个。JSON 格式的`解释`暴露了`选择`类型作为 a 的属性`查询块`, 除非是`简单的`要么`基本的`.JSON 名称（如果适用）也显示在表中。

    | `选择类型`价值 | JSON 名称 | 意义 |
    | -------- | ------- | --- |
    | `简单的` | 没有 | 简单的[`选择`](select.html)（不使用[`联盟`](union.html)或子查询） |
    | `基本的` | 没有 | 最外层[`选择`](select.html) |
    | [`联盟`](union.html) | 没有 | 第二次或以后[`选择`](select.html)中的声明[`联盟`](union.html) |
    | `附属工会` | `依赖的` (`真的`) | 第二次或以后[`选择`](select.html)中的声明[`联盟`](union.html), 依赖于外部查询 |
    | `联合结果` | `union_result` | 结果[`联盟`](union.html). |
    | [`子查询`](optimizer-hints.html#optimizer-hints-subquery) | 没有 | 第一的[`选择`](select.html)在子查询中 |
    | `依赖子查询` | `依赖的` (`真的`) | 第一的[`选择`](select.html)在子查询中，依赖于外部查询 |
    | `衍生的` | 没有 | 派生表 |
    | `相关派生` | `依赖的` (`真的`) | 派生表依赖于另一个表 |
    | `物化` | `materialized_from_subquery` | 物化子查询 |
    | `无法缓存的子查询` | `可缓存的` (`错误的`) | 一个子查询，其结果不能被缓存，必须为外部查询的每一行重新计算 |
    | `不可缓存的联合` | `可缓存的` (`错误的`) | 第二个或以后的选择[`联盟`](union.html)属于不可缓存的子查询（请参阅`无法缓存的子查询`) |

    `依赖`通常表示使用相关子查询。看[第 13.2.11.7 节，“相关子查询”](correlated-subqueries.html).

    `依赖子查询`评价不同于`无法缓存的子查询`评估。为了`依赖子查询`，子查询仅针对其外部上下文中变量的每组不同值重新评估一次。为了`无法缓存的子查询`，对外部上下文的每一行重新评估子查询。

    当您指定`格式=JSON`和`解释`，输出没有直接等价于的单一属性`选择类型`;这`查询块`属性对应于给定的`选择`.属性相当于大多数`选择`刚刚显示的子查询类型是可用的（一个例子是`materialized_from_subquery`为了`物化`)，并在适当时显示。没有 JSON 等价物`简单的`要么`基本的`.

    这`选择类型`非价值[`选择`](select.html)statements 显示受影响表的语句类型。例如，`选择类型`是`删除`为了[`删除`](delete.html)陈述。

-   [](<>) `桌子`（JSON 名称：`表名`)

    输出行所引用的表的名称。这也可以是以下值之一：

    -   `<工会*`米`*,*`ñ`*>`：该行是指与`id`的值*`米`*和*`ñ`*.

    -   `<衍生*`ñ`*>`：该行是指带有`id`的价值*`ñ`*.例如，派生表可能来自`从`条款。

    -   `<子查询*`ñ`*>`：该行是指对具有`id`的价值*`ñ`*.看[第 8.2.2.2 节，“通过实现优化子查询”](subquery-materialization.html).

-   [](<>) `分区`（JSON 名称：`分区`)

    查询将匹配记录的分区。值为`空值`对于非分区表。看[第 24.3.5 节，“获取有关分区的信息”](partitioning-info.html).

-   [](<>) `类型`（JSON 名称：`访问类型`)

    联接类型。有关不同类型的说明，请参阅[`解释`加入类型](explain-output.html#explain-join-types).

-   [](<>) `可能的键`（JSON 名称：`可能的键`)

    这`可能的键`column 表示 MySQL 可以选择从中查找该表中的行的索引。请注意，此列完全独立于输出中显示的表格顺序[`解释`](explain.html).这意味着其中的一些键`可能的键`在实践中可能无法使用生成的表顺序。

    如果此列是`空值`（或在 JSON 格式的输出中未定义），没有相关的索引。在这种情况下，您可以通过检查`在哪里`子句来检查它是否引用了一些适合索引的列。如果是这样，请创建一个适当的索引并检查查询[`解释`](explain.html)再次。看[第 13.1.9 节，“ALTER TABLE 语句”](alter-table.html).

    要查看表有哪些索引，请使用`显示索引 *`tbl_name`*`.

-   [](<>) `钥匙`（JSON 名称：`钥匙`)

    这`钥匙`列表示 MySQL 实际决定使用的键（索引）。如果 MySQL 决定使用其中一种`可能的键`索引来查找行，该索引被列为键值。

    它可能是`钥匙`可以命名一个不存在于`可能的键`价值。如果没有`可能的键`索引适合查找行，但查询选择的所有列都是其他索引的列。也就是说，命名索引覆盖了选定的列，因此虽然它不用于确定要检索哪些行，但索引扫描比数据行扫描更有效。

    为了`InnoDB`，即使查询还选择了主键，二级索引也可能覆盖选定的列，因为`InnoDB`将主键值与每个二级索引一起存储。如果`钥匙`是`空值`, MySQL 没有找到用于更有效地执行查询的索引。

    强制 MySQL 使用或忽略列表中列出的索引`可能的键`列，使用`力指数`,`使用索引`， 要么`忽略索引`在您的查询中。看[第 8.9.4 节，“索引提示”](index-hints.html).

    为了`MyISAM`表，运行[`分析表`](analyze-table.html)帮助优化器选择更好的索引。为了`MyISAM`表，[**myisamchk——分析**](myisamchk.html)做同样的事情。看[第 13.7.3.1 节，“ANALYZE TABLE 语句”](analyze-table.html)， 和[第 7.6 节，“MyISAM 表维护和崩溃恢复”](myisam-table-maintenance.html).

-   [](<>) `key_len`（JSON 名称：`密钥长度`)

    这`key_len`列指示 MySQL 决定使用的密钥的长度。的价值`key_len`使您能够确定 MySQL 实际使用的多部分密钥的多少部分。如果`钥匙`专栏说`空值`， 这`key_len`专栏还说`空值`.

    由于密钥存储格式的原因，对于可以存储的列，密钥长度要大一`空值`比一个`非空`柱子。

-   [](<>) `参考`（JSON 名称：`参考`)

    这`参考`column 显示哪些列或常量与`钥匙`列从表中选择行。

    如果值为`功能`，使用的值是某个函数的结果。要查看哪个功能，请使用[`显示警告`](show-warnings.html)下列的[`解释`](explain.html)看到扩展[`解释`](explain.html)输出。该函数实际上可能是一个运算符，例如算术运算符。

-   [](<>) `行`（JSON 名称：`行`)

    这`行`列表示 MySQL 认为它必须检查以执行查询的行数。

    为了[`InnoDB`](innodb-storage-engine.html)表中，这个数字是一个估计值，可能并不总是准确的。

-   [](<>) `过滤`（JSON 名称：`过滤`)

    这`过滤`列表示按表条件过滤的表行的估计百分比。最大值为 100，这意味着没有过滤行。从 100 开始减小的值表示过滤量增加。`行`显示检查的估计行数和`行`×`过滤`显示与下表连接的行数。例如，如果`行`是 1000 并且`过滤`为 50.00 (50%)，与下表连接的行数为 1000 × 50% = 500。

-   [](<>) `额外的`（JSON 名称：无）

    此列包含有关 MySQL 如何解析查询的附加信息。有关不同值的说明，请参见[`解释`额外的信息](explain-output.html#explain-extra-information).

    没有一个 JSON 属性对应于`额外的`柱子;但是，此列中可能出现的值会显示为 JSON 属性，或显示为`信息`财产。

#### 解释连接类型

这`类型`列[`解释`](explain.html)输出描述了表是如何连接的。在 JSON 格式的输出中，这些是作为`访问类型`财产。以下列表描述了连接类型，按从最佳到最差的顺序排列：

-   [](<>) [`系统`](explain-output.html#jointype_system)

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

    该表只有一行（= 系统表）。这是一种特殊情况[`常量`](explain-output.html#jointype_const)加入类型。

-   [](<>) [`常量`](explain-output.html#jointype_const)

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

    该表最多有一个匹配行，在查询开始时读取。因为只有一行，所以该行中列的值可以被优化器的其余部分视为常量。[`常量`](explain-output.html#jointype_const)表非常快，因为它们只被读取一次。

    [`常量`](explain-output.html#jointype_const)当您比较 a 的所有部分时使用`首要的关键`要么`独特`常数值的索引。在以下查询中，*`tbl_name`*可以用作[`常量`](explain-output.html#jointype_const)桌子：

    ```
    SELECT * FROM tbl_name WHERE primary_key=1;

    SELECT * FROM tbl_name
      WHERE primary_key_part1=1 AND primary_key_part2=2;
    ```

-   [](<>) [`eq_ref`](explain-output.html#jointype_eq_ref)

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

    对于先前表中的每个行组合，从该表中读取一行。除了[`系统`](explain-output.html#jointype_system)和[`常量`](explain-output.html#jointype_const)类型，这是最好的连接类型。当连接使用索引的所有部分并且索引是`首要的关键`要么`唯一非空`指数。

    [`eq_ref`](explain-output.html#jointype_eq_ref)可用于使用比较的索引列`=`操作员。比较值可以是常量或使用在此表之前读取的表中的列的表达式。在以下示例中，MySQL 可以使用[`eq_ref`](explain-output.html#jointype_eq_ref)加入处理*`ref_table`*：

    ```
    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;

    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1=other_table.column
      AND ref_table.key_column_part2=1;
    ```

-   [](<>) [`参考`](explain-output.html#jointype_ref)

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

    对于先前表中的每个行组合，从该表中读取具有匹配索引值的所有行。[`参考`](explain-output.html#jointype_ref)如果连接仅使用键的最左侧前缀或键不是`首要的关键`要么`独特`索引（换句话说，如果连接不能根据键值选择单行）。如果使用的键只匹配几行，这是一个很好的连接类型。

    [`参考`](explain-output.html#jointype_ref)可用于使用比较的索引列`=`要么`<=>`操作员。在以下示例中，MySQL 可以使用[`参考`](explain-output.html#jointype_ref)加入处理*`ref_table`*：

    ```
    SELECT * FROM ref_table WHERE key_column=expr;

    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;

    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1=other_table.column
      AND ref_table.key_column_part2=1;
    ```

-   [](<>) [`全文`](explain-output.html#jointype_fulltext)

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

    连接是使用`全文`指数。

-   [](<>) [`ref_or_null`](explain-output.html#jointype_ref_or_null)

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

    这种连接类型就像[`参考`](explain-output.html#jointype_ref)，但除了 MySQL 会额外搜索包含`空值`价值观。这种连接类型优化最常用于解析子查询。在以下示例中，MySQL 可以使用[`ref_or_null`](explain-output.html#jointype_ref_or_null)加入处理*`ref_table`*：

    ```
    SELECT * FROM ref_table
      WHERE key_column=expr OR key_column IS NULL;
    ```

    看[第 8.2.1.15 节，“IS NULL 优化”](is-null-optimization.html).

-   [](<>) [`index_merge`](explain-output.html#jointype_index_merge)

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

    此连接类型表明使用了索引合并优化。在这种情况下，`钥匙`输出行中的列包含使用的索引列表，并且`key_len`包含所用索引的最长关键部分的列表。有关详细信息，请参阅[第 8.2.1.3 节，“索引合并优化”](index-merge-optimization.html).

-   [](<>) [`唯一子查询`](explain-output.html#jointype_unique_subquery)

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

    这种类型取代[`eq_ref`](explain-output.html#jointype_eq_ref)对于一些`在`以下形式的子查询：

    ```
    value IN (SELECT primary_key FROM single_table WHERE some_expr)
    ```

    [`唯一子查询`](explain-output.html#jointype_unique_subquery)只是一个索引查找功能，完全替代子查询以提高效率。

-   [](<>) [`index_subquery`](explain-output.html#jointype_index_subquery)

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

    这种连接类型类似于[`唯一子查询`](explain-output.html#jointype_unique_subquery).它取代`在`子查询，但它适用于以下形式的子查询中的非唯一索引：

    ```
    value IN (SELECT key_column FROM single_table WHERE some_expr)
    ```

-   [](<>) [`范围`](explain-output.html#jointype_range)

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

    仅检索给定范围内的行，使用索引选择行。这`钥匙`输出行中的列指示使用了哪个索引。这`key_len`包含使用的最长的关键部分。这`参考`列是`空值`对于这种类型。

    [`范围`](explain-output.html#jointype_range)可以在使用任何[`=`](comparison-operators.html#operator_equal), [`<>`](comparison-operators.html#operator_not-equal), [`>`](comparison-operators.html#operator_greater-than), [`>=`](comparison-operators.html#operator_greater-than-or-equal), [`<`](comparison-operators.html#operator_less-than), [`<=`](comparison-operators.html#operator_less-than-or-equal), [`一片空白`](comparison-operators.html#operator_is-null), [`<=>`](comparison-operators.html#operator_equal-to), [`之间`](comparison-operators.html#operator_between), [`喜欢`](string-comparison-functions.html#operator_like)， 要么[`在（）`](comparison-operators.html#operator_in)运营商：

    ```
    SELECT * FROM tbl_name
      WHERE key_column = 10;

    SELECT * FROM tbl_name
      WHERE key_column BETWEEN 10 and 20;

    SELECT * FROM tbl_name
      WHERE key_column IN (10,20,30);

    SELECT * FROM tbl_name
      WHERE key_part1 = 10 AND key_part2 IN (10,20,30);
    ```

-   [](<>) [`指数`](explain-output.html#jointype_index)

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

    这`指数`连接类型与[`全部`](explain-output.html#jointype_all)，除了索引树被扫描。这有两种方式：

    -   如果索引是查询的覆盖索引并且可以用于满足表中所需的所有数据，则仅扫描索引树。在这种情况下，`额外的`专栏说`使用索引`.仅索引扫描通常比[`全部`](explain-output.html#jointype_all)因为索引的大小通常小于表数据。

    -   使用从索引中读取以按索引顺序查找数据行来执行全表扫描。`使用指数`没有出现在`额外的`柱子。

        当查询仅使用属于单个索引的列时，MySQL 可以使用此连接类型。

-   [](<>) [`全部`](explain-output.html#jointype_all)

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

    对先前表中的每个行组合进行全表扫描。如果表是第一个未标记的表，这通常不好[`常量`](explain-output.html#jointype_const), 通常*非常*在所有其他情况下都不好。通常，您可以避免[`全部`](explain-output.html#jointype_all)通过添加索引，这些索引可以根据先前表中的常量值或列值从表中检索行。

#### 解释额外信息

这`额外的`列[`解释`](explain.html)输出包含有关 MySQL 如何解析查询的附加信息。下面的列表解释了可以出现在此列中的值。每个项目还为 JSON 格式的输出指示哪个属性显示`额外的`价值。对于其中一些，有一个特定的属性。其他显示为`信息`财产。

如果您想尽可能快地进行查询，请注意`额外的`的列值`使用文件排序`和`使用临时`, 或者, JSON 格式的`解释`输出，对于`using_filesort`和`using_temporary_table`属性等于`真的`.

-   `向后索引扫描`（JSON：`后向索引扫描`)

    优化器能够使用降序索引`InnoDB`桌子。一起显示`使用索引`.有关详细信息，请参阅[第 8.3.13 节，“降序索引”](descending-indexes.html).

-   `'* 的孩子`桌子`*' 推送加入@1`（JSON：`信息`文本）

    此表被引用为*`桌子`*在一个可以下推到 NDB 内核的连接中。仅在启用下推连接时适用于 NDB Cluster。见说明[`ndb_join_pushdown`](mysql-cluster-options-variables.html#sysvar_ndb_join_pushdown)服务器系统变量以获取更多信息和示例。

-   `未找到常量行`（JSON 属性：`const_row_not_found`)

    对于查询，例如`选择 ... 从 *`tbl_name`*`，桌子是空的。

-   `删除所有行`（JSON 属性：`信息`)

    为了[`删除`](delete.html)，一些存储引擎（如[`MyISAM`](myisam-storage-engine.html)) 支持以简单快速的方式删除所有表行的处理程序方法。这`额外的`如果引擎使用此优化，则会显示值。

-   `清楚的`（JSON 属性：`清楚的`)

    MySQL 正在寻找不同的值，因此它在找到第一个匹配行后停止为当前行组合搜索更多行。

-   `第一场比赛（*`tbl_name`*)`（JSON 属性：`first_match`)

    semijoin FirstMatch 连接快捷策略用于*`tbl_name`*.

-   `对 NULL 键进行全扫描`（JSON 属性：`信息`)

    当优化器无法使用索引查找访问方法时，子查询优化会发生这种情况作为回退策略。

-   `不可能拥有`（JSON 属性：`信息`)

    这`拥有`子句始终为 false 并且不能选择任何行。

-   `不可能在哪里`（JSON 属性：`信息`)

    这`在哪里`子句始终为 false 并且不能选择任何行。

-   `阅读 const 表后注意到不可能的 WHERE`（JSON 属性：`信息`)

    MySQL已阅读所有[`常量`](explain-output.html#jointype_const)（和[`系统`](explain-output.html#jointype_system)) 表并注意`在哪里`子句总是假的。

-   `松散扫描（*`米`*..*`n`*)`（JSON 属性：`信息`)

    使用半连接 LooseScan 策略。*`米`*和*`n`*是关键零件编号。

-   `没有匹配的最小/最大行`（JSON 属性：`信息`)

    没有行满足查询的条件，例如`选择 MIN(...) 从 ... WHERE *`健康）状况`*`.

-   `const 表中没有匹配的行`（JSON 属性：`信息`)

    对于带有连接的查询，有一个空表或没有满足唯一索引条件的行的表。

-   `分区修剪后没有匹配的行`（JSON 属性：`信息`)

    为了[`删除`](delete.html)要么[`更新`](update.html)，优化器在分区修剪后没有发现要删除或更新的内容。它的含义类似于`不可能在哪里`为了[`选择`](select.html)陈述。

-   `没有使用表`（JSON 属性：`信息`)

    查询没有`从`子句，或有一个`从双`条款。

    为了[`插入`](insert.html)要么[`代替`](replace.html)陈述，[`解释`](explain.html)没有时显示此值[`选择`](select.html)部分。例如，它显示为`解释插入 t 值 (10)`因为这相当于`解释插入 t 从 DUAL 中选择 10`.

-   `不存在`（JSON 属性：`信息`)

    MySQL 能够做到`左连接`对查询进行优化，并且在找到与`左连接`标准。以下是可以通过这种方式优化的查询类型的示例：

    ```
    SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
      WHERE t2.id IS NULL;
    ```

    假使，假设`t2.id`定义为`非空`.在这种情况下，MySQL 扫描`t1`并查找行`t2`使用的值`t1.id`.如果 MySQL 在`t2`, 它知道`t2.id`永远不可能`空值`，并且不会扫描中的其余行`t2`有相同的`id`价值。换句话说，对于每一行`t1`, MySQL 只需要在`t2`，无论有多少行实际匹配`t2`.

    在 MySQL 8.0.17 及更高版本中，这也可以表示一个`在哪里`表格条件`不在（*`子查询`*)`要么`不存在 （*`子查询`*)`已在内部转换为反连接。这将删除子查询并将其表带入最顶层查询的计划中，从而提供改进的成本计划。通过合并半连接和反连接，优化器可以更自由地重新排序执行计划中的表，在某些情况下会产生更快的计划。

    您可以通过检查`信息`从列`显示警告`执行后`解释`，或在输出中`解释格式=树`.

    笔记

    反连接是半连接的补充`*`表\_a`* 加入 *`表\_b`* 在 *`健康）状况`*`.反连接返回所有行*`表_a`*有*不*排在*`表_b`*哪个匹配*`健康）状况`*.

-   `计划还没有准备好`（JSON 属性：无）

    这个值出现在[`解释连接`](explain-for-connection.html)当优化器尚未完成为在命名连接中执行的语句创建执行计划时。如果执行计划输出包含多行，则其中任何一个或全部都可以有这个`额外的`值，取决于优化器确定完整执行计划的进度。

-   `检查每条记录的范围（索引图：*`ñ`*)`（JSON 属性：`信息`)

    MySQL 没有找到可以使用的好的索引，但发现某些索引可能会在之前表中的列值已知后使用。对于前面表格中的每个行组合，MySQL 会检查是否可以使用[`范围`](explain-output.html#jointype_range)要么[`index_merge`](explain-output.html#jointype_index_merge)检索行的访问方法。这不是很快，但比执行完全没有索引的连接要快。适用性标准如中所述[第 8.2.1.2 节，“范围优化”](range-optimization.html)， 和[第 8.2.1.3 节，“索引合并优化”](index-merge-optimization.html)，但上表的所有列值都是已知的并被视为常量。

    索引从 1 开始编号，顺序相同，如下所示[`显示索引`](show-index.html)为表。索引映射值*`ñ`*是一个位掩码值，指示哪些索引是候选索引。例如，一个值为`0x19`(二进制 11001) 表示考虑索引 1、4 和 5。

-   `递归的`（JSON 属性：`递归的`)

    这表明该行适用于递归[`选择`](select.html)递归公用表表达式的一部分。看[第 13.2.15 节，“WITH（公用表表达式）”](with.html).

-   `重新实现`（JSON 属性：`重新实现`)

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

    `重新实现 (X,...)`显示在`解释`表格行`吨`， 在哪里`X`是任何横向派生表，当新行`吨`被读取。例如：

    ```
    SELECT
      ...
    FROM
      t,
      LATERAL (derived table that refers to t) AS dt
    ...
    ```

    派生表的内容被重新实现，以使其每次更新`吨`由顶部查询处理。

-   `扫描 *`ñ`* 数据库`（JSON 属性：`信息`)

    这表明服务器在处理查询时执行了多少目录扫描`INFORMATION_SCHEMA`表，如中所述[第 8.2.3 节，“优化信息\_SCHEMA 查询”](information-schema-optimization.html).的价值*`ñ`*可以是 0、1 或`全部`.

-   `选择优化掉的表`（JSON 属性：`信息`)

    优化器确定 1) 最多应该返回一行，以及 2) 要生成这一行，必须读取一组确定性的行。当在优化阶段可以读取要读取的行时（例如，通过读取索引行），在查询执行期间不需要读取任何表。

    当查询被隐式分组时满足第一个条件（包含聚合函数但没有`通过...分组`条款）。当每个使用的索引执行一次行查找时，满足第二个条件。读取的索引数决定了要读取的行数。

    考虑以下隐式分组查询：

    ```
    SELECT MIN(c1), MIN(c2) FROM t1;
    ```

    假设`最小（c1）`可以通过读取一个索引行来检索，并且`最小（c2）`可以通过从不同索引中读取一行来检索。也就是说，对于每一列`c1`和`c2`，存在一个索引，其中列是索引的第一列。在这种情况下，通过读取两个确定性行来返回一行。

    这`额外的`如果要读取的行不确定，则不会出现值。考虑这个查询：

    ```
    SELECT MIN(c2) FROM t1 WHERE c1 <= 10;
    ```

    假设`(c1, c2)`是一个覆盖指数。使用此索引，所有行`c1 <= 10`必须扫描以找到最小值`c2`价值。相比之下，考虑这个查询：

    ```
    SELECT MIN(c2) FROM t1 WHERE c1 = 10;
    ```

    在这种情况下，第一个索引行`c1 = 10`包含最小值`c2`价值。只需读取一行即可生成返回的行。

    对于维护每个表的精确行数的存储引擎（例如`MyISAM`， 但不是`InnoDB`）， 这`额外的`值可能发生在`数数（*）`查询`在哪里`子句丢失或始终为真，并且没有`通过...分组`条款。（这是一个隐式分组查询的实例，其中存储引擎影响是否可以读取确定的行数。）

-   `跳过打开表`,`Open_frm_only`,`打开_full_table`（JSON 属性：`信息`)

    这些值表示适用于查询的文件打开优化`INFORMATION_SCHEMA`表。

    -   `跳过打开表`: 表格文件不需要打开。该信息已从数据字典中获得。

    -   `Open_frm_only`：表信息只需要读取数据字典。

    -   `打开_full_table`: 未优化的信息查找。表信息必须从数据字典中读取并通过读取表文件。

-   `临时开始`,`暂时结束`（JSON 属性：`信息`)

    这表明临时表用于 semijoin Duplicate Weedout 策略。

-   `未找到唯一行`（JSON 属性：`信息`)

    对于查询，例如`选择 ... 从 *`tbl_name`*`, 没有行满足 a 的条件`独特`索引或`首要的关键`在桌子上。

-   `使用文件排序`（JSON 属性：`using_filesort`)

    MySQL 必须做一个额外的过程来找出如何按排序顺序检索行。排序是通过根据连接类型遍历所有行并为匹配的所有行存储排序键和指向该行的指针来完成的。`在哪里`条款。然后对键进行排序，并按排序顺序检索行。看[第 8.2.1.16 节，“按优化排序”](order-by-optimization.html).

-   `使用索引`（JSON 属性：`使用索引`)

    仅使用索引树中的信息从表中检索列信息，而无需执行额外的查找来读取实际行。当查询仅使用属于单个索引的列时，可以使用此策略。

    为了`InnoDB`具有用户定义的聚集索引的表，即使在以下情况下也可以使用该索引`使用索引`不在`额外的`柱子。如果是这种情况`类型`是[`指数`](explain-output.html#jointype_index)和`钥匙`是`基本的`.

    显示有关使用的任何覆盖索引的信息`解释格式=传统`和`解释格式=JSON`.从 MySQL 8.0.27 开始，它也显示为`解释格式=树`.

-   `使用索引条件`（JSON 属性：`using_index_condition`)

    通过访问索引元组并首先对其进行测试以确定是否读取完整的表行来读取表。这样，除非有必要，否则索引信息用于延迟（“下推”）读取全表行。看[第 8.2.1.6 节，“索引条件下推优化”](index-condition-pushdown-optimization.html).

-   `使用索引进行分组`（JSON 属性：`using_index_for_group_by`)

    类似于`使用索引`表访问方法，`使用索引进行分组`表示 MySQL 找到了一个索引，可以用来检索一个索引的所有列`通过...分组`要么`清楚的`无需对实际表进行任何额外磁盘访问的查询。此外，索引以最有效的方式使用，因此对于每个组，只读取几个索引条目。有关详细信息，请参阅[第 8.2.1.17 节，“按优化分组”](group-by-optimization.html).

-   `使用索引进行跳过扫描`（JSON 属性：`using_index_for_skip_scan`)

    表示使用了跳过扫描访问方法。看[跳过扫描范围访问方法](range-optimization.html#range-access-skip-scan).

-   `使用连接缓冲区（块嵌套循环）`,`使用连接缓冲区（批量密钥访问）`,`使用连接缓冲区（哈希连接）`（JSON 属性：`using_join_buffer`)

    来自早期连接的表被部分读入连接缓冲区，然后从缓冲区中使用它们的行来执行与当前表的连接。`（块嵌套循环）`表示使用块嵌套循环算法，`（批量密钥访问）`表示使用批量密钥访问算法，并且`（哈希连接）`表示使用散列连接。也就是说，从表的前一行的表中的键[`解释`](explain.html)输出被缓冲，匹配的行从表中分批获取`使用连接缓冲区`出现。

    在 JSON 格式的输出中，值`using_join_buffer`总是其中之一`块嵌套循环`,`批量密钥访问`， 要么`哈希连接`.

    哈希连接从 MySQL 8.0.18 开始可用；在 MySQL 8.0.20 或更高版本的 MySQL 中不使用 Block Nested-Loop 算法。有关这些优化的更多信息，请参阅[第 8.2.1.4 节，“哈希连接优化”](hash-joins.html)， 和[块嵌套循环加入算法](nested-loop-joins.html#block-nested-loop-join-algorithm).

    看[批量密钥访问连接](bnl-bka-optimization.html#bka-optimization)，有关批量密钥访问算法的信息。

-   `使用 MRR`（JSON 属性：`信息`)

    使用多范围读取优化策略读取表。看[第 8.2.1.11 节，“多范围读取优化”](mrr-optimization.html).

-   `使用 sort_union(...)`,`使用联合（...）`,`使用 intersect(...)`（JSON 属性：`信息`)

    这些表示特定算法，显示如何合并索引扫描[`index_merge`](explain-output.html#jointype_index_merge)加入类型。看[第 8.2.1.3 节，“索引合并优化”](index-merge-optimization.html).

-   `使用临时`（JSON 属性：`using_temporary_table`)

    为了解析查询，MySQL 需要创建一个临时表来保存结果。如果查询包含`通过...分组`和`订购方式`以不同方式列出列的子句。

-   `使用哪里`（JSON 属性：`附加条件`)

    一种`在哪里`子句用于限制与下一个表匹配或发送到客户端的行。除非您特别打算从表中获取或检查所有行，否则您的查询可能有问题，如果`额外的`价值不是`使用哪里`并且表连接类型是[`全部`](explain-output.html#jointype_all)要么[`指数`](explain-output.html#jointype_index).

    `使用哪里`在 JSON 格式的输出中没有直接对应项；这`附加条件`属性包含任何`在哪里`条件：二手。

-   `在推送条件下使用 where`（JSON 属性：`信息`)

    此项目适用于[`新开发银行`](mysql-cluster.html)表*只要*.这意味着 NDB Cluster 正在使用 Condition Pushdown 优化来提高非索引列和常量之间直接比较的效率。在这种情况下，条件被“下推”到集群的数据节点，并同时在所有数据节点上进行评估。这消除了通过网络发送不匹配行的需要，并且可以将此类查询速度提高 5 到 10 倍，比可以使用但不使用条件下推的情况。有关详细信息，请参阅[第 8.2.1.5 节，“发动机工况下推优化”](engine-condition-pushdown-optimization.html).

-   `零极限`（JSON 属性：`信息`)

    查询有一个`限制 0`子句并且不能选择任何行。

#### EXPLAIN 输出解释

您可以通过获取`行`的列[`解释`](explain.html)输出。这应该大致告诉您 MySQL 必须检查多少行才能执行查询。如果您使用[`max_join_size`](server-system-variables.html#sysvar_max_join_size)系统变量，此行积也用于确定哪个多表[`选择`](select.html)要执行的语句以及要中止的语句。看[第 5.1.1 节，“配置服务器”](server-configuration.html).

以下示例显示了如何根据提供的信息逐步优化多表连接[`解释`](explain.html).

假设你有[`选择`](select.html)此处显示的声明，并且您计划使用[`解释`](explain.html)：

```
EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
               tt.ProjectReference, tt.EstimatedShipDate,
               tt.ActualShipDate, tt.ClientID,
               tt.ServiceCodes, tt.RepetitiveID,
               tt.CurrentProcess, tt.CurrentDPPerson,
               tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
               et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
          AND tt.ActualPC = et.EMPLOYID
          AND tt.AssignedPC = et_1.EMPLOYID
          AND tt.ClientID = do.CUSTNMBR;
```

对于此示例，请做出以下假设：

-   被比较的列已声明如下。

    | 桌子 | 柱子 | 数据类型 |
    | --- | --- | ---- |
    | `tt` | `实际PC` | `字符(10)` |
    | `tt` | `分配的PC` | `字符(10)` |
    | `tt` | `客户编号` | `字符(10)` |
    | `等` | `雇员` | `字符(15)` |
    | `做` | `CUSTNMBR` | `字符(15)` |

-   这些表具有以下索引。

    | 桌子 | 指数 |
    | --- | --- |
    | `tt` | `实际PC` |
    | `tt` | `分配的PC` |
    | `tt` | `客户编号` |
    | `等` | `雇员`（首要的关键） |
    | `做` | `CUSTNMBR`（首要的关键） |

-   这`tt.ActualPC`值分布不均。

    最初，在执行任何优化之前，[`解释`](explain.html)语句产生以下信息：


```
table type possible_keys key  key_len ref  rows  Extra
et    ALL  PRIMARY       NULL NULL    NULL 74
do    ALL  PRIMARY       NULL NULL    NULL 2135
et_1  ALL  PRIMARY       NULL NULL    NULL 74
tt    ALL  AssignedPC,   NULL NULL    NULL 3872
           ClientID,
           ActualPC
      Range checked for each record (index map: 0x23)
```

因为`类型`是[`全部`](explain-output.html#jointype_all)对于每个表，此输出表明 MySQL 正在生成所有表的笛卡尔积；也就是说，行的每个组合。这需要相当长的时间，因为必须检查每个表中行数的乘积。对于手头的情况，这个产品是 74 × 2135 × 74 × 3872 = 45,268,558,720 行。如果桌子更大，您只能想象需要多长时间。

这里的一个问题是，如果将列声明为相同的类型和大小，MySQL 可以更有效地使用列上的索引。在这种情况下，[`VARCHAR`](char.html)和[`字符`](char.html)如果它们被声明为相同的大小，则被认为是相同的。`tt.ActualPC`被声明为`字符(10)`和`et.EMPLOYID`是`字符(15)`，所以存在长度不匹配。

要修复列长度之间的这种差异，请使用[`更改表`](alter-table.html)延长`实际PC`从 10 个字符到 15 个字符：

```
mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);
```

现在`tt.ActualPC`和`et.EMPLOYID`都是`VARCHAR(15)`.执行[`解释`](explain.html)语句再次产生这个结果：

```
table type   possible_keys key     key_len ref         rows    Extra
tt    ALL    AssignedPC,   NULL    NULL    NULL        3872    Using
             ClientID,                                         where
             ActualPC
do    ALL    PRIMARY       NULL    NULL    NULL        2135
      Range checked for each record (index map: 0x1)
et_1  ALL    PRIMARY       NULL    NULL    NULL        74
      Range checked for each record (index map: 0x1)
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC 1
```

这并不完美，但要好得多：`行`值小 74 倍。这个版本在几秒钟内执行。可以进行第二次更改以消除列长度不匹配的

tt.AssignedPC = et_1.EMPLOYID`和`tt.ClientID = do.CUSTNMBR`比较：`修改之后，

```
mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
                      MODIFY ClientID   VARCHAR(15);
```

解释[`产生此处显示的输出：`](explain.html)在这一点上，查询几乎被尽可能地优化了。

```
table type   possible_keys key      key_len ref           rows Extra
et    ALL    PRIMARY       NULL     NULL    NULL          74
tt    ref    AssignedPC,   ActualPC 15      et.EMPLOYID   52   Using
             ClientID,                                         where
             ActualPC
et_1  eq_ref PRIMARY       PRIMARY  15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY  15      tt.ClientID   1
```

剩下的问题是，默认情况下，MySQL 假定tt.ActualPC`列是均匀分布的，但情况并非如此`tt`桌子。`幸运的是，告诉 MySQL 分析密钥分布很容易：有了额外的索引信息，连接就完美了

```
mysql> ANALYZE TABLE tt;
```

解释[`产生这个结果：`](explain.html)这

```
table type   possible_keys key     key_len ref           rows Extra
tt    ALL    AssignedPC    NULL    NULL    NULL          3872 Using
             ClientID,                                        where
             ActualPC
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC   1
et_1  eq_ref PRIMARY       PRIMARY 15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY 15      tt.ClientID   1
```

[](<>)行`输出中的列`解释[`是来自 MySQL 连接优化器的有根据的猜测。`](explain.html)通过比较来检查数字是否更接近事实行`具有查询返回的实际行数的产品。`如果数字完全不同，您可能会通过使用获得更好的性能STRAIGHT_JOIN`在你的`选择[`语句并尝试在`](select.html)从`条款。`（然而，`STRAIGHT_JOIN`可能会阻止使用索引，因为它禁用了半连接转换。看[第 8.2.2.1 节，“使用半连接转换优化 IN 和 EXISTS 子查询谓词”](semijoins.html).)

在某些情况下，可以执行修改数据的语句[`解释选择`](explain.html)与子查询一起使用；有关详细信息，请参阅[第 13.2.11.8 节，“派生表”](derived-tables.html).
