MySQL查询计划
环境准备 MySQL 8.0.26(linux版本)
本文用到的数据库表参见:https://blog.csdn.net/qq_40926260/article/details/142445464
EXPLAIN
参考:https://dev.mysql.com/doc/refman/8.4/en/explain.html
EXPLAIN 是 MySQL 中的一个非常有用的命令,用于获取 MySQL 如何执行 SELECT 语句的详细信息,它可以帮助分析查询的执行计划,从而识别出性能瓶颈所在,同时可帮助了解 MySQL 是如何解析 SQL 语句、如何连接表、以及如何选择索引的。
使用方法:在 SQL 查询语句前加上 EXPLAIN 关键字,比如explain select * from students;结果如下
mysql> explain select * from students;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | students | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | NULL |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
注意:
- EXPLAIN 只能与 SELECT, DELETE, INSERT, REPLACE 和 UPDATE 这些sql语句一起
- 对于查询sql,EXPLAIN会生成额外的执行计划信息,可以通过SHOW WARNINGS进行查看
输出格式
参考资料:https://dev.mysql.com/doc/refman/8.4/en/explain-output.html
EXPLAIN结果默认是以表格形式进行展示的,也可以选择以JSON形式展示
FORMAT 选项可用于选择输出格式。TRADITIONAL 以表格格式显示输出。如果没有 FORMAT 选项,则这是默认设置。JSON 格式以 JSON 格式显示信息。
mysql> explain format=JSON select * from students\G;
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "0.75"
},
"table": {
"table_name": "students",
"access_type": "ALL",
"rows_examined_per_scan": 5,
"rows_produced_per_join": 5,
"filtered": "100.00",
"cost_info": {
"read_cost": "0.25",
"eval_cost": "0.50",
"prefix_cost": "0.75",
"data_read_per_join": "10K"
},
"used_columns": [
"student_id",
"student_name",
"gender",
"birth_date",
"class_id",
"phone_number",
"email",
"address",
"enrollment_year",
"school_id"
]
}
}
}
1 row in set, 1 warning (0.00 sec)
表格形式和JSON形式看到的数据有重合的,也有各自独有的
列含义
利用 EXPLAIN 工具,可以观察到以下内容:
- id:查询的 ID, 该列始终包含一个数字,用于标识行所属的 SELECT。
- select_type:MySQL 会将 SELECT 查询分为简单和主要(复杂)类型,如下表所述。
SIMPLE 查询不包含子查询或 UNION
PRIMARY(复杂)复杂类型可分为三大类:简单子查询、派生表(FROM 子句中的子查询)和 UNION。
DELETE:如果您正在解释 DELETE,则 select_type 将是 DELETE - table:查的哪个表
- partitions:查询访问的分区
- type:使用的 JOIN 类型
- possible_keys:sql可能用到的索引
- key:实际使用到的索引
- key_len:MySQL 选择的索引的长度 — 当 MySQL 选择复合索引时,长度字段是确定该复合索引中有多少列正在使用的唯一方法。
- ref:与索引相比的列
- rows:查询访问的行数 — 在数据库实例内设计索引时,也请留意行列。此列显示 MySQL 访问了多少行以完成请求,这在设计索引时很有用。查询访问的行越少,查询速度就越快。
- filtered:按指定条件过滤的行的百分比,此列显示满足表上某些条件(例如 WHERE 子句或连接条件)的行的百分比的悲观估计。如果将行列乘以此百分比,您将看到 MySQL 估计它将与查询计划中的先前表连接的行数。
- Extra:与查询相关的任何额外信息
id
id字段值为select查询的序列号,表示查询中执行select子句或操作表的顺序,有以下3种情况:
- id相同,执行顺序由上至下,顺序执行
- id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
- id相同和id不同的情况同时存在:先根据大小,大的优先,然后id相同的从上到下顺序执行
第一种:id相同,执行顺序由上到下,执行顺序也可以说是加载表的顺序
比如查询课程id为2的授课教师信息,结果如下:
mysql> EXPLAIN SELECT * from teachers t, courses c
-> WHERE t.teacher_id = c.teacher_id
-> AND c.course_id = 2;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | c | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 1 | SIMPLE | t | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
这里使用的是没有join关键字的连接查询,即没有指定是左连接还是右连接,查了2张表,teachers和courses表,而且注意我这里是teachers写在前,courses表写在后的
这种查询和sql语句中谁写在前,谁在后无关。比如我把条件变成t.teacher_id = 2,就变成先查询teachers表了
mysql> EXPLAIN SELECT * from teachers t, courses c
-> WHERE t.teacher_id = c.teacher_id
-> AND t.teacher_id = 2;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | t | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
第二种:id值不同,id越大越优先查询
比如我们将上面的查询改为子查询的写法
mysql> EXPLAIN SELECT * from teachers
-> WHERE teacher_id = (
-> SELECT teacher_id FROM courses WHERE course_id = 2);
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | PRIMARY | teachers | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | SUBQUERY | courses | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
当遇到嵌套子查询时,会先查内层,再查外层子,所以id=2的查询会先进行
第三种:id值有相同也有不同,结果是前两种方式的综合,id大的优先,剩下的如果id相同则从上往下依次执行
查询某个班级的班主任所教课程信息,采用子查询的方式
mysql> EXPLAIN SELECT c.* from courses c, teachers t
-> WHERE c.teacher_id = t.teacher_id
-> AND t.teacher_id = (SELECT head_teacher_id FROM classes c WHERE class_id = 1);
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | PRIMARY | t | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 1 | PRIMARY | c | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
| 2 | SUBQUERY | c | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
3 rows in set, 1 warning (0.01 sec)
第四种:id的值存在为NULL的情况,当使用了联合查询时,此时table列的值表示<unionM,N>引用了id为M,N的查询中的记录行
mysql> EXPLAIN SELECT * FROM courses c WHERE c.course_id < 4
-> UNION SELECT * FROM courses c WHERE c.course_id > 1;
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | c | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 3 | 100.00 | Using where |
| 2 | UNION | c | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 45 | 100.00 | Using where |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
如果使用UNION ALL,不存在临时表,使用UNION时会进行去重
mysql> EXPLAIN SELECT * FROM courses c WHERE c.course_id < 4
-> UNION ALL SELECT * FROM courses c WHERE c.course_id > 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | PRIMARY | c | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 3 | 100.00 | Using where |
| 2 | UNION | c | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 45 | 100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
select_type
参考:https://dev.mysql.com/doc/refman/8.4/en/explain-output.html#explain_select_type
select_type 表示每个查询的查询类型
- simple:简单查询,查询不包含子查询或者union
- primary:复杂查询中最外层的select,一般来说是外层
- subquery:包含在select中的子查询(不在 from 子句中),非最外层
- derived:包含在 form 子句中的子查询,MySQL 会将结果放在一个临时表中,也称为派生表
- union:在union 中的第二个或之后的 select。
- union result:使用 union 关键词后会生成一个临时表,对于这个临时表的 select 。
如果是JSON格式,这些信息将会存放到query_block属性中
table
表示查询到的行引用的是哪张表的数据,可能是别名,也可能是衍生表,具体有以下几种值:
- <union M,N>:行引用了id为M,N的查询的结果
:该查询引用了id为N的查询结果衍生而来的结果,发生在FROM后面的子查询 :行是指id值为N的行的物化子查询的结果
partitions
分区类型,表示该查询的结果将会从那个分区进行匹配,当表未分区时,该值为NULL
type
这个字段表示join type,描述表是如何被联结的,相关的JSON字段是access_type
该字段常见的值依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL,一般要保证效率的话,要优化我们的语句至少使其达到range级别,如果可能的话做好优化到 ref,出现range一般用于范围查找
possible_keys
主要显示查询可能用到哪些索引来查找,只是可能会使用,并不代表一定会使用
常见值说明:
- NULL: 没有相关索引,如果是 NULL 的话,可以考虑在 where 子句中创建一个适当的索引来提高查询性能,然后继续用 explain 查看其效果;也有可能出现 possible_keys 为 NULL,但是 key 有值,实际走了索引。
- 有列值:如果显示表中的某列,则表示可能会走这一列对应列值的索引;如果 possible_keys 有值,但是 key 显示 NULL,这种情况一般存在于表中数据量不大的情况,因为 MySQL 语句优化器认为索引对此查询的帮助不大,从而选择了全表查询。
注意:possible_keys的值与explain输出结果表中表的顺序是完全独立的,这表示possible_keys中的一些键在生成的表顺序中实际上可能不可用
key
key 表示MySQL实际采用哪个索引来优化对该表的查询。如果没有使用索引,则该列为NULL,如果想强制MySQL使用或忽略possible_keys列中的索引,可以在查询中使用force index或ignore index
如果MySQL决定使用一个possible_keys中的索引来查找行,则该索引将作为key字段的值列出
有可能key列的值列出来possible_keys列中不存在的一个索引
key_len
显示了 MySQL 索引所使用的字节数,用于判断复合索引是否被完全使用,通过这个数值可以计算具体使用了索引中的哪些列(主要用于联合索引的优化)。
注意索引最大长度是 768 字节,当字符串过长时,MySQL 会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。
key_len 的计算规则如下:
字符串
常见的是 char(n) 和 varchar(n),从 MySQL 5.0.3 之后,n 均表示字符数,而不是字节数,如果是 UTF-8,一个数字或字母占1个字节,一个汉字占3个字节。
char(n) 非汉字长度为 n,如果存放汉字长度为 3n 字节
varchar(n) 非汉字长度为 n+2,如果存放汉字长度为 3n+2 字节;因为 varchar 是可变长字符串,需要 2 字节来存储字符串长度
数值类型
tinyint 长度为 1 字节
smallint 长度为 2 字节
int 长度为 4 字节
bigint 长度为 8 字节
时间类型
date 长度为 3 字节
timestamp 长度为 4 字节
datetime 长度为 8 字节
NULL
如果字段允许设置为 NULL,则需要 1 字节来记录是否为 NULL; Not NULL 的列则不需要。
ref
显示了在使用 key 列中实际的索引时,表查找时所用到的列名和常量;常见的为 const 常量或索引关联查询的字段(列)名。
key 列是实际使用的 index 。 但 index 可能建立在数据表的若干列上。ref 列列出具体哪些列或常数被使用了。
rows
rows的值可以粗略地告诉您MySQL执行该查询必须检查多少行,并不是真正的结果集中的记录行数,仅供参考
例如下面这个查询,course_id是主键,主键进行等值匹配只会匹配一行,rows=1表示只需要读取1行就可以完成该查询
mysql> EXPLAIN SELECT * FROM courses c WHERE course_id = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | c | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
filtered
表示被过滤了的数据占检查的数据的百分比,即满足WHERE子句条件的行占所有检查的行的百分比。这个值是一个理论估计值,最大值为100,意味着没有对行进行过滤。MySQL.5.7 后,默认 explain 直接显示 partitions 和 filtered 的信息。
rows列表示估计要检查多少行,rows × filtered 显示了剩下的数据行数。例如,如果 rows 为 1000,filtered 为 50.00(50%),则剩下的数据行数为 1000 × 50% = 500,有500行数据被查询条件过滤掉
举个例子,此时courses中有5行数据,course_name值都不同
mysql> EXPLAIN SELECT * FROM courses c WHERE course_name = 's';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
使用course_name进行等值匹配,不管能不能匹配成功,最多只能过滤一行,而表中有5行数据,因此filtered = 1 / 5 = 20%
如果像下面这样就变成40了,因为根据2个值来进行匹配,理论上是可以对2行数据进行过滤的
mysql> EXPLAIN SELECT * FROM courses c WHERE course_name IN ('s', 's');
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 40.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
filtered = 100 并不表示这个查询是性能差的,只是表示所有检查的行都满足where条件而已
Extra
这一列展示的是额外的信息,存在很多值,且在不同的场景下以及不同版本的 MySQL 所表示的意思也不同,只能是表示大概的意思并且仅做优化参考。包含不适合在其他列中显示但十分重要的额外信息
Join 类型
https://dev.mysql.com/doc/refman/8.4/en/explain-output.html#explain-join-types
type列描述了表是如何连接的。下面以此从优到劣介绍t ype 列可能出现的各种值:
system
该表只有一行(=系统表)。这是const连接类型的一个特例。
DROP TABLE IF EXISTS `t`;
CREATE TABLE `t`(
id int(11) AUTO_INCREMENT PRIMARY KEY NOT NULL
) ENGINE = InnoDB DEFAULT CHARSET=utf8;
INSERT INTO t VALUES(NULL);
实际上新建一个只有1行数据的表,type也不一定会是 system
mysql> EXPLAIN SELECT id FROM t;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set (0.03 sec)
而如果 t 表没有主键
DROP TABLE IF EXISTS `t`;
CREATE TABLE `t`(
id int(11)
);
INSERT INTO t VALUES(1);
同样执行查询,结果是ALL
mysql> EXPLAIN SELECT id FROM t;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set (0.04 sec)
const
表示查询结果最多只有一个匹配行。因为只有一行,所以该行列中的值可以被优化器的其余部分视为常量。const表示读取表非常快,因为该表只被读取一次。一般用于将PRIMARY KEY或UNIQUE索引的所有部分与常数值进行比较的场景,例如
SELECT * FROM tbl_name WHERE primary_key = 1;
SELECT * FROM tbl_name
WHERE primary_key_part1 = 1 AND primary_key_part2 = 2;
eq_ref
对于前几张表中的每一行组合,都会从该表中只读取一行,通常是等值匹配。除了system和const类型之外,这是最好的连接类型。当 join 使用索引的所有部分并且索引是PRIMARY KEY或UNIQUE NOT NULL索引时,会使用eq_ref。
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
对于前面表中的每一行组合,将从该表中读取具有匹配索引值的所有行。如果连接仅使用键的最左侧前缀,或者索引不是PRIMARY key或UNIQUE索引,换句话说,如果join无法根据key选择单行,则使用ref。如果使用的索引key只会匹配几行,则这是一种很好的连接类型。
ref可用于使用=或<=>运算符进行比较的索引列。在以下示例中,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;
和eq_ref的区别就是eq_ref是只读该表的1行,而ref是多行
fulltext
表示这个join操作用到了FULLTEXT索引
ref_or_null
这种连接类型类似于ref,但MySQL会额外搜索包含NULL值的行。这种连接类型优化最常用于解决子查询。在以下示例中,MySQL可以使用ref_or_null连接来处理ref_table:
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;
参考MySQL针对IS NULL的优化
https://dev.mysql.com/doc/refman/8.4/en/is-null-optimization.html
index_merge
此连接类型表示使用了索引合并优化。在这种情况下,输出行中的key列会包含所使用的索引列表,key_len包含所使用索引的最长的key部分列表。
https://dev.mysql.com/doc/refman/8.4/en/index-merge-optimization.html
unique_subquery
可以理解为子查询场景下的eq_ref
value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery只是一个索引查找函数,它完全替换了子查询以提高效率。
index_subquery
类似于unique_subquery,但是适用于非唯一索引,可以理解为子查询场景下的 ref 类型
value IN (SELECT key_column FROM single_table WHERE some_expr)
range 索引范围扫描
使用了索引进行范围查询。输出行中的key列会指示使用了哪个索引。key_len包含所使用的最长的key部分。此类型的ref列为NULL。
当使用=、<>、>、>=、<、<=、is NULL、<=>、BETWEEN、LIKE或IN()运算符中的任何一个将键列与常量进行比较时,会使用range
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这个join类型与ALL相同,区别在于index扫描的是索引树。这有两种方式:
- 如果这个索引符合覆盖索引场景,即只扫描该索引就可以满足表中所需的所有数据,则只扫描索引树。在这种情况下,Extra列显示Using index。索引全扫描通常比ALL更快,因为索引的大小通常小于表数据。
- 使用从索引中读取的数据按索引顺序查找数据行来执行全表扫描,此时Extra列中不会出现Using index信息
ALL
表示优化器认为该查询需要进行全表扫描,通常可以通过添加索引来避免ALL。
EXPLAIN扩展信息
https://dev.mysql.com/doc/refman/8.4/en/explain-extended.html
EXPLAIN 语句会生成额外的扩展信息,这些信息 EXPLAIN 的结果里看不到,但可以通过在 EXPLAIN 后执行 SHOW WARNINGS 语句来查看。扩展信息适用于 SELECT、DELETE、INSERT、REPLACE 和 UPDATE 语句。
举个例子
其实前面很多sql执行时,结果都会显示有警告信息的,不过可能没注意。例如下面这条sql:
EXPLAIN SELECT * from teachers
WHERE teacher_id = (
SELECT teacher_id FROM courses WHERE course_id = 2);

可以通过SHOW WARNINGS来查看
mysql> SHOW WARNINGS\G;
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select '2' AS `teacher_id`,'Bob Smith' AS
`teacher_name`,'234-567-8901' AS `phone_number`,'bob.smith@riverside.edu' AS `email`,'Associate Professor' AS `title`,'Physics' AS `field_of_expertise`,'2' AS `school_id` from `student_management`.`teachers` where true
1 row in set (0.00 sec)
ERROR:
No query specified
参考

浙公网安备 33010602011771号