查询中包含子查询的情况:explain SELECT * FROM test1 WHERE id IN(SELECT id FROM test2);
查询中包含 UNION / UNION ALL 语句的情况:EXPLAIN SELECT * FROM test1 UNION ALL SELECT * FROM test2;
查询语句中每出现一个 SELECT 关键字,MySQL 就会为它分配一个唯一的 id 值。这个 id 值就是 EXPLAIN 语句的第一列的值,并且 id 的顺序是按 SELECT 出现的顺序增长的,id 列越大执行优先级越高,id 相同则从上往下执行,id 为 NULL 最后执行。 单 SELECT 关键字
比如下边这个查询中只有一个 SELECT 关键字,所以 EXPLAIN 的结果中也就只有一条 id 列为 1 的记录。 连接查询 对于连接查询来说,一个 SELECT 关键字后边的 FROM 子句中可以跟随多个表,所以在连接查询的执行计划中,每个表都会对应一条记录,但是这些记录的 id 值都是相同的。
可以看到,上述连接查询中参与连接的 test1 和 test2 表分别对应一条记录,但是这两条记录对应的 id 值都是 1,在连接查询的执行计划中,每个表都会对应一条记录,这些记录的 id 列的值是相同的。 包含子查询
对于包含子查询的查询语句来说,就可能涉及多个 SELECT 关键字,所以在包含子查询的查询语句的执行计划中,每个 SELECT 关键字都会对应一个唯一的 id 值。
但是这里需要特别注意,查询优化器可能对涉及子查询的查询语句进行重写,从而转换为连接查询。所以如果我们想知道查询优化器对某个包含子查询的语句是否进行了重写,直接查看执行计划。
可以看到,虽然查询语句是一个子查询,但是执行计划中 test1 和 test2 表对应的记录的 id 值全部是 1,这就表明了查询优化器将子查询转换为了连接查询。 包含 UNION \ UNION ALL 子句
对于包含 UNION 子句的查询语句来说,每个 SELECT 关键字对应一个 id 值也是没错的,不过还是有点儿特别。
这个语句的执行计划的第三条记录是因为 UNION 子句会把多个查询的结果集合并起来并对结果集中的记录进行去重,MySQL 使用的是内部的临时表。UNION 子句是为了把 id 为 1 的查询和 id 为 2 的查询的结果集合并起来并去重,所以在内部创建了一个名为 的临时表,就是执行计划第三条记录的 table 列的名称,id 为 NULL 表明这个临时表是为了合并两个查询的结果集而创建的。
跟 UNION 对比起来 UNION ALL 就不需要为最终的结果集进行去重,它只是单纯的把多个查询的结果集中的记录合并成一个并返回给用户,所以也就不需要使用临时表。所以在包含 UNION ALL 子句的查询的执行计划中,就没有那个 id 为 NULL 的记录。
select_type
一条大的查询语句里边可以包含若干个 SELECT 关键字,每个 SELECT 关键字代表着一个小的查询语句,而每个 SELECT 关键字的 FROM 子句中都可以包含若干张表,每一张表都对应着执行计划输出中的一条记录,对于在同一个 SELECT 关键字中的表来说,它们的 id 值是相同的。
MySQL 为每一个 SELECT 关键字代表的小查询都定义了一个称之为:select_type 的属性,意思是我们只要知道了某个小查询的 select_type 属性,就知道了这个小查询在整个大查询中扮演了一个什么角色,select_type 取值如下:
SIMPLE:简单的 SELECT 查询,不使用 union 及子查询;
PRIMARY:最外层的 SELECT 查询;
UNION:UNION 中的第二个或随后的 SELECT 查询,不依赖于外部查询的结果集;
UNION RESULT:UNION 结果集;
SUBQUERY:子查询中的第一个 SELECT 查询,不依赖于外部查询的结果集;
DERIVED: 用于 FROM 子句里有子查询的情况,MySQL 会递归执行这些子查询,把结果放在临时表里;
SIMPLE
简单的 select 查询,查询中不包含子查询或者 UNION。
连接查询也算是 SIMPLE 类型。 PRIMARY
对于包含 UNION、UNION ALL 或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询的 select_type 值就是 PRIMARY。
从结果中可以看到,最左边的小查询 SELECT * FROMN test1 对应的是执行计划中的第一条记录,它的 select_type 值就是 PRIMARY。 UNION
对于包含 UNION 或者 UNION ALL 的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的查询的 select_type 值就是 UNION。 UNION RESULT
MySQL 选择使用临时表来完成 UNION 查询的去重工作,针对该临时表的查询的 select_type 就是 UNION RESULT,如上图。 SUBQUERY
包含在 SELECT 中的子查询,不在 FROM 子句中。 DERIVED
包含在 FROM 子句中的子查询。MySQL 会将结果存放在一个临时表中,也称为派生表。
从执行计划中可以看出, id 为 2 的记录就代表子查询的执行方式,它的 select_type 是 DERIVED ,说明该子查询是以派生表的方式执行的。id 为 1 的记录代表外层查询,注意看它的 table 列显示的是 ,表示该查询是针对将派生表之后的表进行查询的。
table
Extra 列是用来说明一些额外信息的,可以通过这些额外信息来更准确的理解 MySQL 到底将如何执行给定的查询语句。MySQL 提供的额外信息很多,常见的重要值如下: Using index:当我们的查询列表以及搜索条件中只包含属于某个索引的列,也就是在可以使用索引覆盖的情况下,在 Extra 列将会提示该额外信息。
这个查询中只需要用到 u_idx_day_status 而不需要回表操作。 Using where:当我们使用全表扫描来执行对某个表的查询,并且该语句的 WHERE 子句中有针对该表的搜索条件时,在 Extra 列中会提示。
Using where 只是表示 MySQL 使用 where 子句中的条件对记录进行了过滤。 Using index condition:有些搜索条件中虽然出现了索引列,但却不能使用到索引。
其中的 order_no >'z' 可以使用到索引,但是 order_no LIKE '%a' 却无法使用到索引,在以前版本的 MySQL 中,是按照下边步骤来执行这个查询的:
对于指定的二级索引记录,先不着急回表,而是先检测一下该记录是否满足 order_no LIKE '%a' 这个条件,如果这个条件不满足,则该二级索引记录压根儿就没必要回表;
对于满足 order_no LIKE '%a' 这个条件的二级索引记录执行回表操作,回表操作其实是一个随机 IO 比较耗时;
所以上述修改可以省去很多回表操作的成本,这个改进称之为索引条件下推。
如果在查询语句的执行过程中将要使用索引条件下推这个特性,在 Extra 列中将会显示 Using index condition。 Using temporary:在许多查询的执行过程中,MySQL 可能会借助临时表来完成一些功能,比如去重、排序之类的,比如在执行许多包含 DISTINCT、GROUPBY、UNION 等子句的查询过程中,如果不能有效利用索引来完成查询,MySQL 很有可能寻求通过建立内部的临时表来执行查询。如果查询中使用到了内部的临时表,在执行计划的 Extra 列将会显示 Using temporary。
上边的 GROUP BY 的执行计划的 Extra 列不仅仅包含 Using temporary 提示,还包含 Using filesort 提示,可是查询语句中明明没有写 ORDER BY 子句,这是因为 MySQL 会在包含 GROUP BY 子句的查询中默认添加上 ORDER BY 子句。
如果不想为包含 GROUP BY 子句的查询进行排序,需要显式的写上 ORDER BY NULL。 Using filesort:有一些情况下对结果集中的记录进行排序是可以使用到索引的。
这个查询语句可以利用 idx_order_no 索引直接取出 order_no 列的 10 条记录,然后再进行回表操作。但是很多情况下排序操作无法使用到索引,只能在内存中或者磁盘中进行排序,MySQL 把这种在内存中或者磁盘上进行排序的方式统称为文件排序。如果某个查询需要使用文件排序的方式执行查询,就会在执行计划的Extra 列中显示 Using filesort。 Select tables optimized away:使用某些聚合函数,比如:max、min 来访问存在索引的某个字段是。