Documentation Home
MySQL 8.0 参考手册  / 第8章优化  / 8.8 了解查询执行计划  /  8.8.2 EXPLAIN 输出格式

8.8.2 EXPLAIN 输出格式

EXPLAIN语句提供有关 MySQL 如何执行语句的信息。 EXPLAIN适用于 SELECT, DELETE, INSERT, REPLACE, 和 UPDATE语句。

EXPLAINSELECT为语句中使用的每个表返回一行信息 。它按照 MySQL 在处理语句时读取它们的顺序列出输出中的表。MySQL 使用嵌套循环连接方法解析所有连接。这意味着 MySQL 从第一个表中读取一行,然后在第二个表、第三个表等中找到匹配的行。当所有的表都被处理完后,MySQL 将选择的列输出,并在表列表中回溯,直到找到一个有更多匹配行的表。从此表中读取下一行,然后继续处理下一个表。

EXPLAIN输出包括分区信息。此外,对于SELECT 语句,EXPLAIN生成可​​以显示的扩展信息 SHOW WARNINGSEXPLAIN请参阅 第 8.8.3 节,“扩展 EXPLAIN 输出格式”)。

笔记

在较旧的 MySQL 版本中,分区和扩展信息是使用 EXPLAIN PARTITIONS和 生成的EXPLAIN EXTENDED。这些语法仍然被认为是为了向后兼容,但分区和扩展输出现在默认启用,因此PARTITIONSEXTENDED关键字是多余的并且已弃用。它们的使用会导致警告;EXPLAIN希望在未来的 MySQL 版本中将 它们从语法中删除。

您不能在同一 语句中同时使用 deprecatedPARTITIONS 和关键字。此外,这两个关键字都不能与 选项一起使用。 EXTENDEDEXPLAINFORMAT

笔记

MySQL Workbench 具有 Visual Explain 功能,可提供 EXPLAIN输出的可视化表示。请参阅 教程:使用 Explain 提高查询性能

EXPLAIN 输出列

本节介绍 生成的输出列 EXPLAIN。后面的部分提供了有关 typeExtra 列的附加信息。

每个输出行都EXPLAIN 提供有关一个表的信息。每行包含 表 8.1“EXPLAIN 输出列”中汇总的值,并在表后进行了更详细的描述。列名显示在表的第一列中;第二列提供了 FORMAT=JSON使用时输出中显示的等效属性名称。

表 8.1 EXPLAIN 输出列

柱子 JSON 名称 意义
id select_id SELECT标识符_
select_type 没有任何 SELECT类型_
table table_name 输出行的表
partitions partitions 匹配的分区
type access_type 连接类型
possible_keys possible_keys 可供选择的指标
key key 实际选择的指标
key_len key_length 所选密钥的长度
ref ref 与索引相比的列
rows rows 估计要检查的行
filtered filtered 按表条件过滤的行的百分比
Extra 没有任何 附加信息

笔记

NULL不显示在 JSON 格式EXPLAIN 输出 中的 JSON 属性。

  • id(JSON 名称 select_id:)

    SELECT标识符 。这是查询中的序号 SELECTNULL如果该行引用其他行的并集结果,则该值可以是。在这种情况下,该 table列显示一个值 like 表示该行引用具有值 和 的行的 并 集。 <unionM,N>idMN

  • select_type(JSON 名称:无)

    的类型SELECT,可以是下表中显示的任何一种。JSON 格式将类型EXPLAIN公开 SELECT为 a 的属性 query_block,除非它是 SIMPLEor PRIMARY。JSON 名称(如果适用)也显示在表中。

    select_type价值 JSON 名称 意义
    SIMPLE 没有任何 简单SELECT(不使用 UNION或子查询)
    PRIMARY 没有任何 最外层SELECT
    UNION 没有任何 中的第二个或后面SELECT的语句 UNION
    DEPENDENT UNION dependent( true) a 中的第二个或后面SELECT的语句 UNION,取决于外部查询
    UNION RESULT union_result 的结果UNION
    SUBQUERY 没有任何 SELECT子查询中的第一个
    DEPENDENT SUBQUERY dependent( true) 首先SELECT在子查询中,依赖于外部查询
    DERIVED 没有任何 派生表
    MATERIALIZED materialized_from_subquery 物化子查询
    UNCACHEABLE SUBQUERY cacheable( false) 无法缓存结果且必须为外部查询的每一行重新评估的子查询
    UNCACHEABLE UNION cacheable( false) UNION 属于不可缓存子查询 的第二个或后面的选择(参见UNCACHEABLE SUBQUERY

    DEPENDENT通常表示使用相关子查询。请参阅 第 13.2.10.7 节,“相关子查询”

    DEPENDENT SUBQUERY评价不同于UNCACHEABLE SUBQUERY评价。对于DEPENDENT SUBQUERY,子查询仅针对来自其外部上下文的每组不同变量值重新评估一次。对于 UNCACHEABLE SUBQUERY,为外部上下文的每一行重新评估子查询。

    子查询的可缓存性不同于查询缓存中查询结果的缓存(在 第 8.10.3.1 节,“查询缓存如何操作”中描述)。子查询缓存发生在查询执行期间,而查询缓存仅在查询执行完成后才用于存储结果。

    当您指定FORMAT=JSONwith EXPLAIN时,输出没有直接等同于 的单个属性 select_type;该 query_block属性对应于给定的SELECT. 与刚刚显示的大多数子查询类型等效的属性SELECT可用(例如 materialized_from_subqueryfor MATERIALIZED),并在适当的时候显示。SIMPLE或没有 JSON 等价物 PRIMARY

    select_type非语句 的值SELECT显示受影响表的语句类型。例如,select_typeis DELETEfor DELETE语句。

  • table(JSON 名称 table_name:)

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

    • <unionM,N>: 该行是指具有 和 id值 的行的M并 集N

    • <derivedN>:该行引用值为 的行的派生表id结果 N。例如,派生表可能来自 FROM子句中的子查询。

    • <subqueryN>:该行引用值为 的行的具体化子查询的id 结果N。请参阅 第 8.2.2.2 节,“使用物化优化子查询”

  • partitions(JSON 名称 partitions:)

    查询将匹配记录的分区。该值适用NULL于非分区表。请参阅 第 22.3.5 节,“获取有关分区的信息”

  • type(JSON 名称 access_type:)

    连接类型。有关不同类型的说明,请参阅 EXPLAIN 联接类型

  • possible_keys(JSON 名称 possible_keys:)

    possible_keys列指示 MySQL 可以从中选择的索引来查找该表中的行。请注意,此列完全独立于 的输出中显示的表的顺序 EXPLAIN。这意味着 中的某些键在possible_keys实践中可能无法用于生成的表顺序。

    如果此列是NULL(或在 JSON 格式的输出中未定义),则没有相关索引。在这种情况下,您可以通过检查WHERE 子句来检查它是否引用适合索引的某个或多个列来提高查询的性能。如果是这样,请创建一个适当的索引并 EXPLAIN再次检查查询。请参阅 第 13.1.8 节,“ALTER TABLE 语句”

    要查看表有哪些索引,请使用. SHOW INDEX FROM tbl_name

  • key(JSON 名称key:)

    key列表示 MySQL 实际决定使用的键(索引)。如果 MySQL 决定使用其中一个possible_keys 索引来查找行,则该索引将作为键值列出。

    可以key命名值中不存在的索引 possible_keys。如果没有possible_keys索引适合查找行,但查询选择的所有列都是某个其他索引的列,则可能会发生这种情况。也就是说,命名索引覆盖了选定的列,因此虽然它不用于确定检索哪些行,但索引扫描比数据行扫描更有效。

    对于InnoDB,即使查询还选择了主键,二级索引也可能覆盖选定的列,因为InnoDB将主键值存储在每个二级索引中。如果 keyNULL,则 MySQL 找不到可用于更有效地执行查询的索引。

    要强制 MySQL 使用或忽略列中列出的索引 ,请在查询中possible_keys使用 FORCE INDEXUSE INDEXIGNORE INDEX。请参阅第 8.9.4 节,“索引提示”

    对于MyISAM表,运行 ANALYZE TABLE有助于优化器选择更好的索引。对于 MyISAM表,myisamchk --analyze做同样的事情。请参阅 第 13.7.2.1 节,“ANALYZE TABLE 语句”第 7.6 节,“MyISAM 表维护和崩溃恢复”

  • key_len(JSON 名称 key_length:)

    key_len列指示 MySQL 决定使用的密钥的长度。的值 key_len使您能够确定 MySQL 实际使用多部分键的多少部分。如果key专栏说 NULLkey_len 专栏也说NULL

    由于密钥存储格式的原因,列的密钥长度可以NULL 比​​列的密钥长度大一个NOT NULL

  • ref(JSON 名称ref:)

    ref列显示将哪些列或常量与列中指定的索引进行比较以 key从表中选择行。

    如果值为func,则使用的值是某个函数的结果。要查看哪个函数,请使用 SHOW WARNINGS以下 EXPLAIN命令查看扩展 EXPLAIN输出。该函数实际上可能是一个运算符,例如算术运算符。

  • rows(JSON 名称 rows:)

    rows列指示 MySQL 认为它必须检查以执行查询的行数。

    对于InnoDB表格,此数字是一个估计值,可能并不总是准确的。

  • filtered(JSON 名称 filtered:)

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

  • Extra(JSON 名称:无)

    此列包含有关 MySQL 如何解析查询的附加信息。有关不同值的说明,请参阅 EXPLAIN 额外信息

    没有对应于该 Extra列的单个 JSON 属性;但是,此列中可能出现的值作为 JSON 属性或属性文本公开message

解释连接类型

输出列描述typeEXPLAIN表是如何连接的。在 JSON 格式的输出中,这些作为access_type属性的值被发现。下面的列表描述了连接类型,从最好的类型到最差的排序:

  • system

    该表只有一行(=系统表)。const这是连接类型 的一个特例 。

  • const

    该表最多有一个匹配行,在查询开始时读取。因为只有一行,所以优化器的其余部分可以将这一行中列的值视为常量。 const表非常快,因为它们只读一次。

    constPRIMARY KEY当您将 a或 UNIQUE索引的所有部分与常量值进行比较时使用。在以下查询中,tbl_name可以用作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

    对于先前表中行的每个组合,从该表中读取一行。除了 systemand const类型,这是最好的连接类型。当连接使用索引的所有部分并且索引是 PRIMARY KEYorUNIQUE NOT NULL索引时使用它。

    eq_ref可用于使用 =运算符进行比较的索引列。比较值可以是常量或表达式,它使用在此表之前读取的表中的列。在以下示例中,MySQL 可以使用 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;
  • ref

    对于先前表中行的每个组合,从该表中读取具有匹配索引值的所有行。ref如果连接仅使用键的最左边前缀,或者如果键不是PRIMARY KEYor UNIQUE索引(换句话说,如果连接不能根据键值选择单个行),则使用。如果使用的键只匹配几行,这是一个很好的连接类型。

    ref可用于使用 =or<=> 运算符进行比较的索引列。在以下示例中,MySQL 可以使用 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;
  • fulltext

    连接是使用FULLTEXT 索引执行的。

  • ref_or_null

    这种连接类型类似于 ,但 MySQL 对包含值ref的行进行了额外的搜索。NULL这种连接类型优化最常用于解析子查询。在以下示例中,MySQL 可以使用 ref_or_null连接来处理ref_table

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

    请参阅第 8.2.1.13 节,“IS NULL 优化”

  • index_merge

    此连接类型表示使用索引合并优化。在这种情况下,key输出行中的列包含使用的索引列表,并 key_len包含使用的索引的最长键部分列表。有关详细信息,请参阅 第 8.2.1.3 节,“索引合并优化”

  • unique_subquery

    此类型替换 以下形式eq_ref的某些 IN子查询:

    value IN (SELECT primary_key FROM single_table WHERE some_expr)

    unique_subquery只是一个索引查找函数,它完全取代了子查询以提高效率。

  • index_subquery

    此联接类型类似于 unique_subquery。它取代了IN子查询,但它适用于以下形式的子查询中的非唯一索引:

    value IN (SELECT key_column FROM single_table WHERE some_expr)
  • range

    仅检索给定范围内的行,使用索引来选择行。输出行中的key 列指示使用了哪个索引。key_len包含使用过的最长的密钥部分。该refNULL适用于此类型。

    range可以在使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, 或 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);
  • index

    连接类型与index相同 ALL,只是扫描了索引树。这有两种情况:

    • 如果索引是查询的覆盖索引,可以用来满足表中所有需要的数据,则只扫描索引树。在这种情况下,该Extra列 显示Using index。仅索引扫描通常比索引扫描更快, ALL因为索引的大小通常小于表数据。

    • 使用从索引读取以按索引顺序查找数据行来执行全表扫描。 Uses index不会出现在 Extra列中。

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

  • ALL

    对先前表中的每个行组合进行全表扫描。如果表是第一个未标记的表 ,这通常不好,在所有其他情况下const通常 非常糟糕。通常,您可以 ALL通过添加索引来避免,这些索引允许根据常量值或早期表中的列值从表中检索行。

解释额外信息

输出列Extra包含 EXPLAIN有关 MySQL 如何解析查询的附加信息。以下列表解释了可以出现在该列中的值。每个项目还为 JSON 格式的输出指示哪个属性显示Extra值。对于其中一些,有一个特定的属性。其他显示为message 属性的文本。

如果您想尽可能快地进行查询,请注意 和 的Extra列值Using filesortUsing temporary或者在 JSON 格式的EXPLAIN输出中, 注意using_filesortusing_temporary_table等于 的属性 true

  • Child of 'table' pushed join@1(JSON:message 文本)

    该表被引用为 table可以向下推送到 NDB 内核的连接中的子表。仅在启用下推连接时适用于 NDB Cluster。ndb_join_pushdown有关更多信息和示例, 请参阅服务器系统变量的描述 。

  • const row not found(JSON 属性 const_row_not_found:)

    对于诸如 之类的查询,表是空的。 SELECT ... FROM tbl_name

  • Deleting all rows(JSON 属性 message:)

    对于DELETE,一些存储引擎(例如MyISAM)支持一种处理程序方法,该方法可以简单快速地删除所有表行。Extra如果引擎使用此优化,则会显示 此值。

  • Distinct(JSON 属性 distinct:)

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

  • FirstMatch(tbl_name) (JSON 属性first_match:)

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

  • Full scan on NULL key(JSON 属性 message:)

    当优化器不能使用索引查找访问方法时,子查询优化会作为后备策略发生这种情况。

  • Impossible HAVING(JSON 属性 message:)

    HAVING子句始终为 false,并且不能选择任何行。

  • Impossible WHERE(JSON 属性 message:)

    WHERE子句始终为 false,并且不能选择任何行。

  • Impossible WHERE noticed after reading const tables(JSON 属性 message:)

    MySQL 已读取所有 const(和 system)表并注意到该WHERE子句始终为假。

  • LooseScan(m..n) (JSON 属性message:)

    使用半连接 LooseScan 策略。 m并且 n是关键部件号。

  • No matching min/max row(JSON 属性 message:)

    没有行满足查询条件,例如 。 SELECT MIN(...) FROM ... WHERE condition

  • no matching row in const table(JSON 属性message:)

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

  • No matching rows after partition pruning(JSON 属性 message:)

    对于DELETEUPDATE,优化器在分区修剪后没有发现要删除或更新的内容。它的含义类似于Impossible WHERE forSELECT语句。

  • No tables used(JSON 属性 message:)

    查询没有FROM子句,或有 FROM DUAL子句。

    对于INSERTor REPLACE语句, EXPLAIN当没有SELECT 部分时显示此值。例如,它出现是EXPLAIN INSERT INTO t VALUES(10)因为它等同于 EXPLAIN INSERT INTO t SELECT 10 FROM DUAL

  • Not exists(JSON 属性 message:)

    MySQL 能够对LEFT JOIN 查询进行优化,并且在找到与条件匹配的行后,不会检查此表中的更多行以查找先前的行组合LEFT JOIN。以下是可以通过这种方式优化的查询类型的示例:

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

    假设t2.id定义为 NOT NULL。在这种情况下,MySQL 使用 的值 扫描t1并查找行 。如果 MySQL 在中找到匹配的行 ,它知道 永远不可能 ,并且不会扫描具有相同值的其余行。换句话说,对于 中的每一行,MySQL 只需要在 中进行一次查找,而不管 中有多少行实际匹配。 t2t1.idt2t2.idNULLt2idt1t2t2

  • Plan isn't ready yet(JSON 属性:无)

    EXPLAIN FOR CONNECTION当优化器尚未完成为在命名连接中执行的语句创建执行计划时, 会出现此值。如果执行计划输出包含多行,则其中任何一行或所有行都可能具有此 Extra值,具体取决于优化程序确定完整执行计划的进度。

  • Range checked for each record (index map: N)(JSON 属性 message:)

    MySQL 没有找到好的索引可以使用,但发现在知道前面表的列值后,可能会使用某些索引。对于前面表中的每个行组合,MySQL 检查是否可以使用rangeindex_merge访问方法来检索行。这不是很快,但比执行完全没有索引的连接要快。适用性标准如 第 8.2.1.2 节“范围优化”第 8.2.1.3 节“索引合并优化”中所述,除了上表的所有列值都是已知的并被视为常量。

    Indexes are numbered beginning with 1, in the same order as shown by SHOW INDEX for the table. The index map value N is a bitmask value that indicates which indexes are candidates. For example, a value of 0x19 (binary 11001) means that indexes 1, 4, and 5 are considered.

  • Scanned N databases (JSON property: message)

    This indicates how many directory scans the server performs when processing a query for INFORMATION_SCHEMA tables, as described in Section 8.2.3, “Optimizing INFORMATION_SCHEMA Queries”. The value of N can be 0, 1, or all.

  • Select tables optimized away (JSON property: message)

    The optimizer determined 1) that at most one row should be returned, and 2) that to produce this row, a deterministic set of rows must be read. When the rows to be read can be read during the optimization phase (for example, by reading index rows), there is no need to read any tables during query execution.

    The first condition is fulfilled when the query is implicitly grouped (contains an aggregate function but no GROUP BY clause). The second condition is fulfilled when one row lookup is performed per index used. The number of indexes read determines the number of rows to read.

    Consider the following implicitly grouped query:

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

    Suppose that MIN(c1) can be retrieved by reading one index row and MIN(c2) can be retrieved by reading one row from a different index. That is, for each column c1 and c2, there exists an index where the column is the first column of the index. In this case, one row is returned, produced by reading two deterministic rows.

    This Extra value does not occur if the rows to read are not deterministic. Consider this query:

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

    Suppose that (c1, c2) is a covering index. Using this index, all rows with c1 <= 10 must be scanned to find the minimum c2 value. By contrast, consider this query:

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

    In this case, the first index row with c1 = 10 contains the minimum c2 value. Only one row must be read to produce the returned row.

    For storage engines that maintain an exact row count per table (such as MyISAM, but not InnoDB), this Extra value can occur for COUNT(*) queries for which the WHERE clause is missing or always true and there is no GROUP BY clause. (This is an instance of an implicitly grouped query where the storage engine influences whether a deterministic number of rows can be read.)

  • Skip_open_table, Open_frm_only, Open_full_table (JSON property: message)

    These values indicate file-opening optimizations that apply to queries for INFORMATION_SCHEMA tables, as described in Section 8.2.3, “Optimizing INFORMATION_SCHEMA Queries”.

    • Skip_open_table: Table files do not need to be opened. The information has already become available within the query by scanning the database directory.

    • Open_frm_only: Only the table's .frm file need be opened.

    • Open_full_table: The unoptimized information lookup. The .frm, .MYD, and .MYI files must be opened.

  • Start temporary, End temporary (JSON property: message)

    This indicates temporary table use for the semijoin Duplicate Weedout strategy.

  • unique row not found (JSON property: message)

    For a query such as SELECT ... FROM tbl_name, no rows satisfy the condition for a UNIQUE index or PRIMARY KEY on the table.

  • Using filesort (JSON property: using_filesort)

    MySQL must do an extra pass to find out how to retrieve the rows in sorted order. The sort is done by going through all rows according to the join type and storing the sort key and pointer to the row for all rows that match the WHERE clause. The keys then are sorted and the rows are retrieved in sorted order. See Section 8.2.1.14, “ORDER BY Optimization”.

  • Using index (JSON property: using_index)

    The column information is retrieved from the table using only information in the index tree without having to do an additional seek to read the actual row. This strategy can be used when the query uses only columns that are part of a single index.

    For InnoDB tables that have a user-defined clustered index, that index can be used even when Using index is absent from the Extra column. This is the case if type is index and key is PRIMARY.

  • Using index condition (JSON property: using_index_condition)

    Tables are read by accessing index tuples and testing them first to determine whether to read full table rows. In this way, index information is used to defer (push down) reading full table rows unless it is necessary. See Section 8.2.1.5, “Index Condition Pushdown Optimization”.

  • Using index for group-by (JSON property: using_index_for_group_by)

    Similar to the Using index table access method, Using index for group-by indicates that MySQL found an index that can be used to retrieve all columns of a GROUP BY or DISTINCT query without any extra disk access to the actual table. Additionally, the index is used in the most efficient way so that for each group, only a few index entries are read. For details, see Section 8.2.1.15, “GROUP BY Optimization”.

  • Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access) (JSON property: using_join_buffer)

    Tables from earlier joins are read in portions into the join buffer, and then their rows are used from the buffer to perform the join with the current table. (Block Nested Loop) indicates use of the Block Nested-Loop algorithm and (Batched Key Access) indicates use of the Batched Key Access algorithm. That is, the keys from the table on the preceding line of the EXPLAIN output are buffered, and the matching rows are fetched in batches from the table represented by the line in which Using join buffer appears.

    In JSON-formatted output, the value of using_join_buffer is always either one of Block Nested Loop or Batched Key Access.

    For more information about these algorithms, see Block Nested-Loop Join Algorithm, and Batched Key Access Joins.

  • Using MRR (JSON property: message)

    Tables are read using the Multi-Range Read optimization strategy. See Section 8.2.1.10, “Multi-Range Read Optimization”.

  • Using sort_union(...), Using union(...), Using intersect(...) (JSON property: message)

    These indicate the particular algorithm showing how index scans are merged for the index_merge join type. See Section 8.2.1.3, “Index Merge Optimization”.

  • Using temporary (JSON property: using_temporary_table)

    To resolve the query, MySQL needs to create a temporary table to hold the result. This typically happens if the query contains GROUP BY and ORDER BY clauses that list columns differently.

  • Using where (JSON property: attached_condition)

    A WHERE clause is used to restrict which rows to match against the next table or send to the client. Unless you specifically intend to fetch or examine all rows from the table, you may have something wrong in your query if the Extra value is not Using where and the table join type is ALL or index.

    Using where has no direct counterpart in JSON-formatted output; the attached_condition property contains any WHERE condition used.

  • Using where with pushed condition (JSON property: message)

    This item applies to NDB tables only. It means that NDB Cluster is using the Condition Pushdown optimization to improve the efficiency of a direct comparison between a nonindexed column and a constant. In such cases, the condition is pushed down to the cluster's data nodes and is evaluated on all data nodes simultaneously. This eliminates the need to send nonmatching rows over the network, and can speed up such queries by a factor of 5 to 10 times over cases where Condition Pushdown could be but is not used. For more information, see Section 8.2.1.4, “Engine Condition Pushdown Optimization”.

  • Zero limit (JSON property: message)

    The query had a LIMIT 0 clause and cannot select any rows.

EXPLAIN Output Interpretation

You can get a good indication of how good a join is by taking the product of the values in the rows column of the EXPLAIN output. This should tell you roughly how many rows MySQL must examine to execute the query. If you restrict queries with the max_join_size system variable, this row product also is used to determine which multiple-table SELECT statements to execute and which to abort. See Section 5.1.1, “Configuring the Server”.

The following example shows how a multiple-table join can be optimized progressively based on the information provided by EXPLAIN.

Suppose that you have the SELECT statement shown here and that you plan to examine it using EXPLAIN:

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;

For this example, make the following assumptions:

  • The columns being compared have been declared as follows.

    Table Column Data Type
    tt ActualPC CHAR(10)
    tt AssignedPC CHAR(10)
    tt ClientID CHAR(10)
    et EMPLOYID CHAR(15)
    do CUSTNMBR CHAR(15)
  • The tables have the following indexes.

    Table Index
    tt ActualPC
    tt AssignedPC
    tt ClientID
    et EMPLOYID (primary key)
    do CUSTNMBR (primary key)
  • The tt.ActualPC values are not evenly distributed.

Initially, before any optimizations have been performed, the EXPLAIN statement produces the following information:

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)

Because type is ALL for each table, this output indicates that MySQL is generating a Cartesian product of all the tables; that is, every combination of rows. This takes quite a long time, because the product of the number of rows in each table must be examined. For the case at hand, this product is 74 × 2135 × 74 × 3872 = 45,268,558,720 rows. If the tables were bigger, you can only imagine how long it would take.

One problem here is that MySQL can use indexes on columns more efficiently if they are declared as the same type and size. In this context, VARCHAR and CHAR are considered the same if they are declared as the same size. tt.ActualPC is declared as CHAR(10) and et.EMPLOYID is CHAR(15), so there is a length mismatch.

To fix this disparity between column lengths, use ALTER TABLE to lengthen ActualPC from 10 characters to 15 characters:

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

Now tt.ActualPC and et.EMPLOYID are both VARCHAR(15). Executing the EXPLAIN statement again produces this result:

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

This is not perfect, but is much better: The product of the rows values is less by a factor of 74. This version executes in a couple of seconds.

A second alteration can be made to eliminate the column length mismatches for the tt.AssignedPC = et_1.EMPLOYID and tt.ClientID = do.CUSTNMBR comparisons:

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

After that modification, EXPLAIN produces the output shown here:

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

At this point, the query is optimized almost as well as possible. The remaining problem is that, by default, MySQL assumes that values in the tt.ActualPC column are evenly distributed, and that is not the case for the tt table. Fortunately, it is easy to tell MySQL to analyze the key distribution:

mysql> ANALYZE TABLE tt;

With the additional index information, the join is perfect and EXPLAIN produces this result:

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

The rows column in the output from EXPLAIN is an educated guess from the MySQL join optimizer. Check whether the numbers are even close to the truth by comparing the rows product with the actual number of rows that the query returns. If the numbers are quite different, you might get better performance by using STRAIGHT_JOIN in your SELECT statement and trying to list the tables in a different order in the FROM clause. (However, STRAIGHT_JOIN may prevent indexes from being used because it disables semijoin transformations. See Section 8.2.2.1, “Optimizing Subqueries, Derived Tables, and View References with Semijoin Transformations”.)

EXPLAIN SELECT与子查询一起使用 时,在某些情况下可以执行修改数据的语句;有关详细信息,请参阅第 13.2.10.8 节,“派生表”