EXPLAIN提供了关于MySQL如何执行语句的信息。EXPLAIN可以用于SELECT、DELETE、INSERT、REPLACE和UPDATE语句。
下面我们新建张表,看下EXPLAIN输出的信息。
#查看mysql版本
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.27 |
+-----------+
1 row in set (0.01 sec)
#新建测试表并插入数据
mysql> CREATE TABLE IF NOT EXISTS `employees_example` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(30) NOT NULL DEFAULT "",
`last_name` varchar(40) NOT NULL DEFAULT "",
`position` varchar(25) NOT NULL DEFAULT "",
`home_address` varchar(50) NOT NULL DEFAULT "",
`home_phone` varchar(12) NOT NULL DEFAULT "",
`employee_code` varchar(25) NOT NULL DEFAULT "",
PRIMARY KEY (`id`),
UNIQUE KEY `employee_code` (`employee_code`),
KEY `phone` (home_phone),
KEY `full_name` (`first_name`,`last_name`)
) ;
INSERT INTO `employees_example` (`first_name`, `last_name`, `position`, `home_address`, `home_phone`, `employee_code`)
VALUES
('Mustapha', 'Mond', 'Chief Executive Officer', '692 Promiscuous Plaza', '326-555-3492', 'MM1'),
('Henry', 'Foster', 'Store Manager', '314 Savage Circle', '326-555-3847', 'HF1'),
('Bernard', 'Marx', 'Cashier', '1240 Ambient Avenue', '326-555-8456', 'BM1'),
('Lenina', 'Crowne', 'Cashier', '281 Bumblepuppy Boulevard', '328-555-2349', 'LC1'),
('Fanny', 'Crowne', 'Restocker', '1023 Bokanovsky Lane', '326-555-6329', 'FC1'),
('Helmholtz', 'Watson', 'Janitor', '944 Soma Court', '329-555-2478', 'HW1');
Query OK, 0 rows affected (0.30 sec)
Query OK, 6 rows affected (0.04 sec)
Records: 6 Duplicates: 0 Warnings: 0
#查看查询语句的执行信息
mysql> explain select * from employees_example;
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | employees_example | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | NULL |
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set (0.06 sec)
#以JSON格式输出查询语句的执行信息
mysql> explain FORMAT=JSON select * from employees_example;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "2.20"
},
"table": {
"table_name": "employees_example",
"access_type": "ALL",
"rows_examined_per_scan": 6,
"rows_produced_per_join": 6,
"filtered": "100.00",
"cost_info": {
"read_cost": "1.00",
"eval_cost": "1.20",
"prefix_cost": "2.20",
"data_read_per_join": "1K"
},
"used_columns": [
"id",
"first_name",
"last_name",
"position",
"home_address",
"home_phone",
"employee_code"
]
}
}
} |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.10 sec)
Explain输出的列和其基本含义
列名 | JSON 格式名称 | 基本含义 |
---|---|---|
id | select_id | 标识符 |
select_type | None | 语句类型 |
table | table_name | 输出结果的表 |
partitions | partitions | 匹配的分区 |
type | access_type | 连接类型 |
possible_keys | possible_keys | 可能选择的索引 |
key | key | 实际用到的索引,NULL表示没有用到索引 |
key_len | key_length | 使用索引的字节数(标识是否只使用多列索引的一部分) |
ref | ref | 表示哪些列或常量被用于用于查找索引列上的值 |
rows | rows | 预估查找出的数据量 |
filtered | filtered | 条件筛选行的百分比 |
Extra | None | 额外信息 |
id:
表示查询中的执行顺序
#id相同,执行顺序由上至下
mysql> explain select * from employees_example t1,employees_example t2,employees_example t3 where t1.id = t2.id and t2.employee_code = t3.employee_code;
+----+-------------+-------+------------+--------+-----------------------+---------------+---------+-----------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+-----------------------+---------------+---------+-----------------------+------+----------+-------+
| 1 | SIMPLE | t1 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 100.00 | NULL |
| 1 | SIMPLE | t2 | NULL | eq_ref | PRIMARY,employee_code | PRIMARY | 4 | test.t1.id | 1 | 100.00 | NULL |
| 1 | SIMPLE | t3 | NULL | eq_ref | employee_code | employee_code | 77 | test.t2.employee_code | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+-----------------------+---------------+---------+-----------------------+------+----------+-------+
3 rows in set (0.06 sec)
#如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
mysql> explain select t1.* from employees_example t1 where t1.id = (select t2.id from employees_example t2 where t2.employee_code = (select t3.employee_code from employees_example t3 where t3.first_name like 'H%' limit 1));
+----+-------------+-------+------------+-------+---------------+---------------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------------+---------+-------+------+----------+-----------------------+
| 1 | PRIMARY | t1 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | SUBQUERY | t2 | NULL | const | employee_code | employee_code | 77 | const | 1 | 100.00 | Using index |
| 3 | SUBQUERY | t3 | NULL | range | full_name | full_name | 92 | NULL | 2 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+---------------+---------+-------+------+----------+-----------------------+
3 rows in set (0.06 sec)
#id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
mysql> explain select t1.* from employees_example t1 where t1.id = (select t2.id from employees_example t2 where t2.employee_code in (select t3.employee_code from employees_example t3 where t3.first_name like 'H%') limit 1);
+----+-------------+-------+------------+--------+-------------------------+---------------+---------+-----------------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+-------------------------+---------------+---------+-----------------------+------+----------+-----------------------+
| 1 | PRIMARY | t1 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | SUBQUERY | t3 | NULL | range | employee_code,full_name | full_name | 92 | NULL | 2 | 100.00 | Using index condition |
| 2 | SUBQUERY | t2 | NULL | eq_ref | employee_code | employee_code | 77 | test.t3.employee_code | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+-------------------------+---------------+---------+-----------------------+------+----------+-----------------------+
3 rows in set (0.06 sec)
select_type:
查询的类型
select_type 的值 | JSON 名称 | 含义 |
---|---|---|
SIMPLE | None | 查询中不包含子查询或者聚合查询(union) |
PRIMARY | None | 查询中若包含任何复杂的子部分,最外层查询为:PRIMARY |
UNION | None | 聚合查询中的第二个或最后一个查询 |
DEPENDENT UNION | dependent (true) | 聚合查询中的第二个或最后一个查询,依赖于外层查询出的数据 |
UNION RESULT | union_result | 聚合查询的结果 |
SUBQUERY | None | 子查询中的第一个查询 |
DEPENDENT SUBQUERY | dependent (true) | 子查询中的第一个查询,依赖于外层查询出的数据 |
DERIVED | None | 衍生表 (若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED) |
MATERIALIZED | materialized_from_subquery | 物化子查询 |
UNCACHEABLE SUBQUERY | cacheable (false) | 无法缓存结果的子查询,必须为其他查询重新评估 |
UNCACHEABLE UNION | cacheable (false) | 聚合查询中的第二个或最后一个查询,无法使用缓存 |
#UNION和SUBQUERY
mysql> explain select t1.* from employees_example t1 where t1.first_name = 'Mustapha' union select t2.* from employees_example t2 where t2.employee_code = (select t3.employee_code from employees_example t3 where t3.employee_code = 'HW1');
+------+--------------+------------+------------+-------+---------------+---------------+---------+-------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+--------------+------------+------------+-------+---------------+---------------+---------+-------+------+----------+-----------------+
| 1 | PRIMARY | t1 | NULL | ref | full_name | full_name | 92 | const | 1 | 100.00 | NULL |
| 2 | UNION | t2 | NULL | const | employee_code | employee_code | 77 | const | 1 | 100.00 | NULL |
| 3 | SUBQUERY | t3 | NULL | const | employee_code | employee_code | 77 | const | 1 | 100.00 | Using index |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+------+--------------+------------+------------+-------+---------------+---------------+---------+-------+------+----------+-----------------+
4 rows in set (0.07 sec)
#DEPENDENT UNION和DEPENDENT SUBQUERY
mysql> explain select t3.* FROM employees_example t3 where t3.id IN (select t1.id from employees_example t1 where t1.first_name = 'Mustapha' union select t2.id from employees_example t2 where t2.employee_code = 'HW1');
+------+--------------------+------------+------------+--------+-----------------------+---------------+---------+-------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+--------------------+------------+------------+--------+-----------------------+---------------+---------+-------+------+----------+-----------------+
| 1 | PRIMARY | t3 | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | t1 | NULL | eq_ref | PRIMARY,full_name | PRIMARY | 4 | func | 1 | 16.67 | Using where |
| 3 | DEPENDENT UNION | t2 | NULL | const | PRIMARY,employee_code | employee_code | 77 | const | 1 | 100.00 | Using index |
| NULL | UNION RESULT | <union2,3> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+------+--------------------+------------+------------+--------+-----------------------+---------------+---------+-------+------+----------+-----------------+
4 rows in set (0.06 sec)
#DERIVED 在from后where前的子查询
mysql> explain select t2.* FROM (select t1.id from employees_example t1 where t1.first_name = 'Mustapha' limit 1) t2;
+----+-------------+------------+------------+--------+---------------+-----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+-----------+---------+-------+------+----------+-------------+
| 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| 2 | DERIVED | t1 | NULL | ref | full_name | full_name | 92 | const | 1 | 100.00 | Using index |
+----+-------------+------------+------------+--------+---------------+-----------+---------+-------+------+----------+-------------+
2 rows in set (0.07 sec)
#MATERIALIZED
mysql> explain select t1.* from employees_example t1 where home_phone in (select home_phone from employees_example where employee_code like 'H%');
+----+--------------+-------------------+------------+-------+---------------------+---------------+---------+------------------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+-------------------+------------+-------+---------------------+---------------+---------+------------------------+------+----------+-----------------------+
| 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | NULL |
| 1 | SIMPLE | t1 | NULL | ref | phone | phone | 38 | <subquery2>.home_phone | 1 | 100.00 | NULL |
| 2 | MATERIALIZED | employees_example | NULL | range | employee_code,phone | employee_code | 77 | NULL | 2 | 100.00 | Using index condition |
+----+--------------+-------------------+------------+-------+---------------------+---------------+---------+------------------------+------+----------+-----------------------+
3 rows in set (0.06 sec)
#UNCACHEABLE SUBQUERY和UNCACHEABLE UNION
mysql> set @var1 = 'Mustapha';
Query OK, 0 rows affected (0.00 sec)
mysql> set @var2 = 'MM1';
Query OK, 0 rows affected (0.00 sec)
mysql> explain select t3.* FROM employees_example t3 where t3.id = (select t1.id from employees_example t1 where t1.first_name = @var1 union select t2.id from employees_example t2 where t2.employee_code = @var2);
+------+----------------------+------------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+----------------------+------------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+
| 1 | PRIMARY | t3 | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using where |
| 2 | UNCACHEABLE SUBQUERY | t1 | NULL | index | NULL | full_name | 214 | NULL | 6 | 100.00 | Using where; Using index |
| 3 | UNCACHEABLE UNION | t2 | NULL | index | NULL | employee_code | 77 | NULL | 6 | 100.00 | Using where; Using index |
| NULL | UNION RESULT | <union2,3> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+------+----------------------+------------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+
4 rows in set (0.05 sec)
table
输出行的来源。
<unionM,N>: 来源于聚合查询,M和N是查询标识符id的值。
<derivedN>: 来源于派生表的结果,N是查询标识符id的值。例如From中的子查询
<subqueryN>: 来源于物化子查询,N是查询标识符id的值。
partitions
查询将匹配记录所在的分区。对于非分区表,该值为NULL。
type
描述表是如何连接,是较为重要的一个指标,结果值从好到坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。
一般来说,得保证查询至少达到range级别,最好能达到ref。
值 | 描述 |
---|---|
system | 只有一行数据的表(=system table),是一个特殊的const类型 |
const | 表中最多有一个匹配的行。因为只有一行,行中的列值将被优化器当作常数。const非常快,因为只读一次。 |
eq_ref | 表之间的组合,行行对应。这是除system和const外最合适的连接类型。通常关联条件为主键和不为NULL的唯一索引。常用=运算符来比较 |
ref | 使用非唯一索引或主键、唯一索引的前缀查询。匹配的行数越少,越合适。 |
fulltext | 使用全文索引 |
ref_or_null | 与ref类似,但是增加了对于NULL值的额外查询。通常发生在子查询中 |
index_merge | 表示使用了索引合并优化方法,key列表示使用了那些索引 |
unique_subquery | 与eq_ref相似,但是使用的是子查询来查找 value IN (SELECT primary_key FROM single_table WHERE some_expr) |
index_subquery | 与ref相似,但是使用的是子查询来查找 value IN (SELECT key_column FROM single_table WHERE some_expr) |
range | 一个或多个索引的范围查询 |
index | 对于索引树的完全扫描,有两种发放重现1、覆盖索引的查询全表(Extra - Using index),比ALL快,因为索引文件较小。2、全表扫描,使用索引进行排序 |
ALL | 全表扫描 |
#ALL
mysql> explain select * from employees_example;
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | employees_example | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100 | NULL |
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set
#index
mysql> explain select employee_code from employees_example;
+----+-------------+-------------------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | employees_example | NULL | index | NULL | employee_code | 77 | NULL | 6 | 100 | Using index |
+----+-------------+-------------------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+
1 row in set
mysql> explain select * from employees_example ORDER BY id desc;
+----+-------------+-------------------+------------+-------+---------------+---------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+---------------+---------+---------+------+------+----------+-------+
| 1 | SIMPLE | employees_example | NULL | index | NULL | PRIMARY | 4 | NULL | 6 | 100 | NULL |
+----+-------------+-------------------+------------+-------+---------------+---------+---------+------+------+----------+-------+
1 row in set
#range
mysql> explain select * from employees_example where id between 1 and 4 and employee_code like 'H%';
+----+-------------+-------------------+------------+-------+-----------------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+-----------------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | employees_example | NULL | range | PRIMARY,employee_code | PRIMARY | 4 | NULL | 4 | 33.33 | Using where |
+----+-------------+-------------------+------------+-------+-----------------------+---------+---------+------+------+----------+-------------+
1 row in set
#ref
mysql> explain select * from employees_example where first_name = 'Mustapha';
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | employees_example | NULL | ref | full_name | full_name | 92 | const | 1 | 100 | NULL |
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set
#eq_ref
mysql> explain select * from employees_example t1, employees_example t2 where t1.id = t2.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------+
| 1 | SIMPLE | t1 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 100 | NULL |
| 1 | SIMPLE | t2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | 100 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------+
2 rows in set
#const和system
mysql> explain select * from(select * from employees_example where id = 1 limit 1) t1;
+----+-------------+-------------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
| 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100 | NULL |
| 2 | DERIVED | employees_example | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100 | NULL |
+----+-------------+-------------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
2 rows in set
possible_keys
MySQL查找数据可能使用到的索引。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。
key
显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。
mysql> explain select id,first_name from employees_example;
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| 1 | SIMPLE | employees_example | NULL | index | NULL | full_name | 214 | NULL | 6 | 100 | Using index |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
1 row in set
key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)。
mysql> explain select * from employees_example where first_name = 'Mustapha';
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | employees_example | NULL | ref | full_name | full_name | 92 | const | 1 | 100 | NULL |
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set
mysql> explain select * from employees_example where first_name = 'Mustapha' and last_name = 'Mond';
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | employees_example | NULL | ref | full_name | full_name | 214 | const,const | 1 | 100 | NULL |
+----+-------------+-------------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
ref
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。
rows
表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。
filtered
显示了通过条件过滤出的行数的预估百分比。
Extra
显示MySQL如何解析查询的额外信息
- Using index
只需要查找索引就可以查找出所需的信息,不需要执行其他查询。(覆盖索引)
mysql> explain select id,first_name,last_name from employees_example;
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| 1 | SIMPLE | employees_example | NULL | index | NULL | full_name | 214 | NULL | 6 | 100 | Using index |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
1 row in set
- Using where
在查找结果之后进行过滤,许多where条件里涉及索引中的列,当它读取索引时,就能被检验,因此不是所有带where字句的查询都会显示"Using where"。
mysql> explain select id,first_name,last_name from employees_example where first_name like 'H%';
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | employees_example | NULL | range | full_name | full_name | 92 | NULL | 2 | 100 | Using where; Using index |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
1 row in set
- Using temporary
需要创建一个临时表来保存结果。通常发生在使用GROUP BY、DISTINCT或ORDER BY时。
mysql> explain select distinct(position) from employees_example where id between 2 and 4;
+----+-------------+-------------------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| 1 | SIMPLE | employees_example | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 3 | 100 | Using where; Using temporary |
+----+-------------+-------------------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
1 row in set
- Using filesort
MySQL中无法利用索引完成的排序操作称为“filesort”
mysql> explain select * from employees_example order by last_name;
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | employees_example | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100 | Using filesort |
+----+-------------+-------------------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set
- Using join buffer
在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该根据查询的具体情况来添加索引来改进能。
mysql> explain select * from employees_example t1 left join employees_example t2 on t1.position = t2.position;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100 | NULL |
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set
- Impossible WHERE、Impossible HAVING
where或having语句始终为false,因此select不会返回任何数据
mysql> explain select * from employees_example where 1 = 2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
1 row in set
mysql> explain select position,count(1) as cnt from employees_example group by position having 1 = 2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible HAVING |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------+
1 row in set
- Using index condition
与“Using where”相似,在索引级别进行内部优化。(索引下推)
mysql> explain
select * from employees_example where first_name like 'H%' and last_name like 'w%';
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | employees_example | NULL | range | full_name | full_name | 214 | NULL | 2 | 16.67 | Using index condition |
+----+-------------+-------------------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
1 row in set
- Using index for group-by
表明可以在索引中找到分组所需的所有数据,不需要查询实际的表 - no matching row in const table
对于只有一条数据的表,没有返回匹配的数据
mysql> explain select * from employees_example where id = 10;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
1 row in set
- No tables used
没有from或from dual语句
mysql> explain select curdate();
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set
- Not exists
查找到一行匹配数据后不再去查询其他数据。通常被用在左联接中,显式地搜索左联接表中不存在的行。SELECT * FROM t1 LEFT JOIN t2 on (…) WHERE t2.not_null_column IS NULL
mysql> explain select * from employees_example t1 left join employees_example t2 on t1.id = t2.id where t2.employee_code is null;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100 | NULL |
| 1 | SIMPLE | t2 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 16.67 | Using where; Not exists; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------------------+
2 rows in set
- Select tables optimized away
这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行。
mysql> explain select max(id) from employees_example;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
1 row in set
- Zero limit
查询有一个LIMIT 0子句并且没有任何数据返回。
mysql> explain select * from employees_example limit 0;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Zero limit |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
1 row in set
- Index merges
当MySQL 决定要在一个给定的表上使用超过一个索引的时候,就会出现以下格式中的一个,详细说明使用的索引以及合并的类型。
Using sort_union(…)
Using union(…)
Using intersect(…)
参考: