explain显示了mysql如何使用索引来处理select语句以及链接表。能够帮助选择更好的索引和写出更优化的查询语句。html
使用方法,在select语句前加上explain就能够了:mysql
如:sql
- explain select surname,first_name form a,b where a.id=b.id
EXPLAIN列的解释:express
table:显示这一行的数据是关于哪张表的mysql优化
type:这是重要的列,显示链接使用了何种类型。从最好到最差的链接类型为const、eq_reg、ref、range、index和ALL函数
type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
通常来讲,得保证查询至少达到range级别,最好能达到ref。性能
possible_keys:显示可能应用在这张表中的索引。若是为空,没有可能的索引。能够为相关的域从WHERE语句中选择一个合适的语句优化
key: 实际使用的索引。若是为NULL,则没有使用索引。不多的状况下,MYSQL会选择优化不足的索引。这种状况下,能够在SELECT语句中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引spa
key_len:使用的索引的长度。在不损失精确性的状况下,长度越短越好设计
ref:显示索引的哪一列被使用了,若是可能的话,是一个常数
rows:MYSQL认为必须检查的用来返回请求数据的行数
Extra:关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里能够看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢
extra列返回的描述的意义
Distinct:一旦MYSQL找到了与行相联合匹配的行,就再也不搜索了
Not exists: MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就再也不搜索了
Range checked for each Record(index map:#):没有找到理想的索引,所以对于从前面表中来的每个行组合,MYSQL检查使用哪一个索引,并用它来从表中返回行。这是使用索引的最慢的链接之一
Using filesort: 看到这个的时候,查询就须要优化了。MYSQL须要进行额外的步骤来发现如何对返回的行排序。它根据链接类型以及存储排序键值和匹配条件的所有行的行指针来排序所有行
Using index: 列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的所有的请求列都是同一个索引的部分的时候
Using temporary 看到这个的时候,查询须要优化了。这里,MYSQL须要建立一个临时表来存储结果,这一般发生在对不一样的列集进行ORDER BY上,而不是GROUP BY上
Where used 使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。若是不想返回表中的所有行,而且链接类型ALL或index,这就会发生,或者是查询有问题不一样链接类型的解释(按照效率高低的顺序排序)
system 表只有一行:system表。这是const链接类型的特殊状况
const:表中的一个记录的最大值可以匹配这个查询(索引能够是主键或唯一索引)。由于只有一行,这个值实际就是常数,由于MYSQL先读这个值而后把它当作常数来对待
eq_ref:在链接中,MYSQL在查询时,从前面的表中,对每个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或唯一键的所有时使用
ref:这个链接类型只有在查询使用了不是唯一或主键的键或者是这些类型的部分(好比,利用最左边前缀)时发生。对于以前的表的每个行联合,所有记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好
range:这个链接类型使用索引返回一个范围中的行,好比使用>或<查找东西时发生的状况
index: 这个链接类型对前面的表中的每个记录联合进行彻底扫描(比ALL更好,由于索引通常小于表数据)
ALL:这个链接类型对于前面的每个记录联合进行彻底扫描,这通常比较糟糕,应该尽可能避免
先看一个例子:
- mysql> explain select * from t_order;
- +----+-------------+---------+------+---------------+------+---------+------+--------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+------+---------------+------+---------+------+--------+-------+
- | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100453 | |
- +----+-------------+---------+------+---------------+------+---------+------+--------+-------+
- 1 row in set (0.03 sec)
加上extended后以后:
- mysql> explain extended select * from t_order;
- +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
- +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+
- | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100453 | 100.00 | |
- +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+
- 1 row in set, 1 warning (0.00 sec)
有必要解释一下这个长长的表格里每一列的含义:
id |
SELECT识别符。这是SELECT的查询序列号 |
select_type |
SELECT类型,能够为如下任何一种:
- SIMPLE:简单SELECT(不使用UNION或子查询)
- PRIMARY:最外面的SELECT
- UNION:UNION中的第二个或后面的SELECT语句
- DEPENDENT UNION:UNION中的第二个或后面的SELECT语句,取决于外面的查询
- UNION RESULT:UNION 的结果
- SUBQUERY:子查询中的第一个SELECT
- DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询
- DERIVED:导出表的SELECT(FROM子句的子查询)
|
table |
输出的行所引用的表 |
type |
联接类型。下面给出各类联接类型,按照从最佳类型到最坏类型进行排序:
- system:表仅有一行(=系统表)。这是const联接类型的一个特例。
- const:表最多有一个匹配行,它将在查询开始时被读取。由于仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,由于它们只读取一次!
- eq_ref:对于每一个来自于前面的表的行组合,从该表中读取一行。这多是最好的联接类型,除了const类型。
- ref:对于每一个来自于前面的表的行组合,全部有匹配索引值的行将从这张表中读取。
- ref_or_null:该联接类型如同ref,可是添加了MySQL能够专门搜索包含NULL值的行。
- index_merge:该联接类型表示使用了索引合并优化方法。
- unique_subquery:该类型替换了下面形式的IN子查询的ref: 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:只检索给定范围的行,使用一个索引来选择行。
- index:该联接类型与ALL相同,除了只有索引树被扫描。这一般比ALL快,由于索引文件一般比数据文件小。
- ALL:对于每一个来自于先前的表的行组合,进行完整的表扫描。
|
possible_keys |
指出MySQL能使用哪一个索引在该表中找到行 |
key |
显示MySQL实际决定使用的键(索引)。若是没有选择索引,键是NULL。 |
key_len |
显示MySQL决定使用的键长度。若是键是NULL,则长度为NULL。 |
ref |
显示使用哪一个列或常数与key一块儿从表中选择行。 |
rows |
显示MySQL认为它执行查询时必须检查的行数。多行之间的数据相乘能够估算要处理的行数。 |
filtered |
显示了经过条件过滤出的行数的百分比估计值。 |
Extra |
该列包含MySQL解决查询的详细信息
- Distinct:MySQL发现第1个匹配行后,中止为当前的行组合搜索更多的行。
- Not exists:MySQL可以对查询进行LEFT JOIN优化,发现1个匹配LEFT JOIN标准的行后,再也不为前面的的行组合在该表内检查更多的行。
- range checked for each record (index map: #):MySQL没有发现好的可使用的索引,但发现若是来自前面的表的列值已知,可能部分索引可使用。
- Using filesort:MySQL须要额外的一次传递,以找出如何按排序顺序检索行。
- Using index:从只使用索引树中的信息而不须要进一步搜索读取实际的行来检索表中的列信息。
- Using temporary:为了解决查询,MySQL须要建立一个临时表来容纳结果。
- Using where:WHERE 子句用于限制哪个行匹配下一个表或发送到客户。
- Using sort_union(...), Using union(...), Using intersect(...):这些函数说明如何为index_merge联接类型合并索引扫描。
- Using index for group-by:相似于访问表的Using index方式,Using index for group-by表示MySQL发现了一个索引,能够用来查 询GROUP BY或DISTINCT查询的全部列,而不要额外搜索硬盘访问实际的表。
|
一.select_type的说明
1.UNION:
当经过union来链接多个查询结果时,第二个以后的select其select_type为UNION。
- mysql> explain select * from t_order where order_id=100 union select * from t_order where order_id=200;
- +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+
- | 1 | PRIMARY | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | |
- | 2 | UNION | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | |
- | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
- +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+
- 3 rows in set (0.34 sec)
2.DEPENDENT UNION与DEPENDENT SUBQUERY:
当union做为子查询时,其中第二个union的select_type就是DEPENDENT UNION。
第一个子查询的select_type则是DEPENDENT SUBQUERY。
- mysql> explain select * from t_order where order_id in (select order_id from t_order where order_id=100 union select order_id from t_order where order_id=200);
- +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+
- | 1 | PRIMARY | t_order | ALL | NULL | NULL | NULL | NULL | 100453 | Using where |
- | 2 | DEPENDENT SUBQUERY | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index |
- | 3 | DEPENDENT UNION | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index |
- | NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | |
- +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+
- 4 rows in set (0.03 sec)
3.SUBQUERY:
子查询中的第一个select其select_type为SUBQUERY。
- mysql> explain select * from t_order where order_id=(select order_id from t_order where order_id=100);
- +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
- | 1 | PRIMARY | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | |
- | 2 | SUBQUERY | t_order | const | PRIMARY | PRIMARY | 4 | | 1 | Using index |
- +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
- 2 rows in set (0.03 sec)
4.DERIVED:
当子查询是from子句时,其select_type为DERIVED。
- mysql> explain select * from (select order_id from t_order where order_id=100) a;
- +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+
- | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
- | 2 | DERIVED | t_order | const | PRIMARY | PRIMARY | 4 | | 1 | Using index |
- +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+
- 2 rows in set (0.03 sec)
二.type的说明
1.system,const
见上面4.DERIVED的例子。其中第一行的type就是为system,第二行是const,这两种联接类型是最快的。
2.eq_ref
在t_order表中的order_id是主键,t_order_ext表中的order_id也是主键,该表能够认为是订单表的补充信息表,他们的关系是1对1,在下面的例子中能够看到b表的链接类型是eq_ref,这是极快的联接类型。
- mysql> explain select * from t_order a,t_order_ext b where a.order_id=b.order_id;
- +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
- | 1 | SIMPLE | b | ALL | order_id | NULL | NULL | NULL | 1 | |
- | 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 4 | test.b.order_id | 1 | Using where |
- +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
- 2 rows in set (0.00 sec)
3.ref
下面的例子在上面的例子上略做了修改,加上了条件。此时b表的联接类型变成了ref。由于全部与a表中order_id=100的匹配记录都将会从b表获取。这是比较常见的联接类型。
- mysql> explain select * from t_order a,t_order_ext b where a.order_id=b.order_id and a.order_id=100;
- +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+
- | 1 | SIMPLE | a | const | PRIMARY | PRIMARY | 4 | const | 1 | |
- | 1 | SIMPLE | b | ref | order_id | order_id | 4 | const | 1 | |
- +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+
- 2 rows in set (0.00 sec)
4.ref_or_null
user_id字段是一个能够为空的字段,并对该字段建立了一个索引。在下面的查询中能够看到联接类型为ref_or_null,这是mysql为含有null的字段专门作的处理。在咱们的表设计中应当尽可能避免索引字段为NULL,由于这会额外的耗费mysql的处理时间来作优化。
- mysql> explain select * from t_order where user_id=100 or user_id is null;
- +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+
- | 1 | SIMPLE | t_order | ref_or_null | user_id | user_id | 5 | const | 50325 | Using where |
- +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+
- 1 row in set (0.00 sec)
5.index_merge
常常出如今使用一张表中的多个索引时。mysql会将多个索引合并在一块儿,以下例:
- mysql> explain select * from t_order where order_id=100 or user_id=10;
- +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+
- | 1 | SIMPLE | t_order | index_merge | PRIMARY,user_id | PRIMARY,user_id | 4,5 | NULL | 2 | Using union(PRIMARY,user_id); Using where |
- +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+
- 1 row in set (0.09 sec)
6.unique_subquery
该联接类型用于替换value IN (SELECT primary_key FROM single_table WHERE some_expr)这样的子查询的ref。注意ref列,其中第二行显示的是func,代表unique_subquery是一个函数,而不是一个普通的ref。
- mysql> explain select * from t_order where order_id in (select order_id from t_order where user_id=10);
- +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+
- | 1 | PRIMARY | t_order | ALL | NULL | NULL | NULL | NULL | 100649 | Using where |
- | 2 | DEPENDENT SUBQUERY | t_order | unique_subquery | PRIMARY,user_id | PRIMARY | 4 | func | 1 | Using where |
- +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+
- 2 rows in set (0.00 sec)
7.index_subquery
该联接类型与上面的太像了,惟一的差异就是子查询查的不是主键而是非惟一索引。
- mysql> explain select * from t_order where user_id in (select user_id from t_order where order_id>10);
- +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+
- | 1 | PRIMARY | t_order | ALL | NULL | NULL | NULL | NULL | 100649 | Using where |
- | 2 | DEPENDENT SUBQUERY | t_order | index_subquery | PRIMARY,user_id | user_id | 5 | func | 50324 | Using index; Using where |
- +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+
- 2 rows in set (0.00 sec)
8.range
按指定的范围进行检索,很常见。
- mysql> explain select * from t_order where user_id in (100,200,300);
- +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
- | 1 | SIMPLE | t_order | range | user_id | user_id | 5 | NULL | 3 | Using where |
- +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
- 1 row in set (0.00 sec)
9.index
在进行统计时很是常见,此联接类型实际上会扫描索引树,仅比ALL快些。
- mysql> explain select count(*) from t_order;
- +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+
- | 1 | SIMPLE | t_order | index | NULL | user_id | 5 | NULL | 100649 | Using index |
- +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+
- 1 row in set (0.00 sec)
10.ALL
完整的扫描全表,最慢的联接类型,尽量的避免。
- mysql> explain select * from t_order;
- +----+-------------+---------+------+---------------+------+---------+------+--------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+------+---------------+------+---------+------+--------+-------+
- | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100649 | |
- +----+-------------+---------+------+---------------+------+---------+------+--------+-------+
- 1 row in set (0.00 sec)
三.extra的说明
1.Distinct
MySQL发现第1个匹配行后,中止为当前的行组合搜索更多的行。对于此项没有找到合适的例子,求指点。
2.Not exists
由于b表中的order_id是主键,不可能为NULL,因此mysql在用a表的order_id扫描t_order表,并查找b表的行时,若是在b表发现一个匹配的行就再也不继续扫描b了,由于b表中的order_id字段不可能为NULL。这样避免了对b表的屡次扫描。
- mysql> explain select count(1) from t_order a left join t_order_ext b on a.order_id=b.order_id where b.order_id is null;
- +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+
- | 1 | SIMPLE | a | index | NULL | express_type | 1 | NULL | 100395 | Using index |
- | 1 | SIMPLE | b | ref | order_id | order_id | 4 | test.a.order_id | 1 | Using where; Using index; Not exists |
- +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+
- 2 rows in set (0.01 sec)
3.Range checked for each record
这种状况是mysql没有发现好的索引可用,速度比没有索引要快得多。
- mysql> explain select * from t_order t, t_order_ext s where s.order_id>=t.order_id and s.order_id<=t.order_id and t.express_type>5;
- +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+
- | 1 | SIMPLE | t | range | PRIMARY,express_type | express_type | 1 | NULL | 1 | Using where |
- | 1 | SIMPLE | s | ALL | order_id | NULL | NULL | NULL | 1 | Range checked for each record (index map: 0x1) |
- +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+
- 2 rows in set (0.00 sec)
4.Using filesort
在有排序子句的状况下很常见的一种状况。此时mysql会根据联接类型浏览全部符合条件的记录,并保存排序关键字和行指针,而后排序关键字并按顺序检索行。
- mysql> explain select * from t_order order by express_type;
- +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+
- | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100395 | Using filesort |
- +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+
- 1 row in set (0.00 sec)
5.Using index
这是性能很高的一种状况。当查询所需的数据能够直接从索引树中检索到时,就会出现。上面的例子中有不少这样的例子,再也不多举例了。
6.Using temporary
发生这种状况通常都是须要进行优化的。mysql须要建立一张临时表用来处理此类查询。
- mysql> explain select * from t_order a left join t_order_ext b on a.order_id=b.order_id group by b.order_id;
- +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+
- | 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 100395 | Using temporary; Using filesort |
- | 1 | SIMPLE | b | ref | order_id | order_id | 4 | test.a.order_id | 1 | |
- +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+
- 2 rows in set (0.00 sec)
7.Using where
当有where子句时,extra都会有说明。
8.Using sort_union(...)/Using union(...)/Using intersect(...)
下面的例子中user_id是一个检索范围,此时mysql会使用sort_union函数来进行索引的合并。而当user_id是一个固定值时,请参看上面type说明5.index_merge的例子,此时会使用union函数进行索引合并。
- mysql> explain select * from t_order where order_id=100 or user_id>10;
- +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+
- | 1 | SIMPLE | t_order | index_merge | PRIMARY,user_id | user_id,PRIMARY | 5,4 | NULL | 2 | Using sort_union(user_id,PRIMARY); Using where |
- +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+
- 1 row in set (0.00 sec)
对于Using intersect的例子能够参看下例,user_id与express_type发生了索引交叉合并。
- mysql> explain select * from t_order where express_type=1 and user_id=100;
- +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+
- | 1 | SIMPLE | t_order | index_merge | user_id,express_type | user_id,express_type | 5,1 | NULL | 1 | Using intersect(user_id,express_type); Using where |
- +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+
- 1 row in set (0.00 sec)
9.Using index for group-by
代表能够在索引中找到分组所需的全部数据,不须要查询实际的表。
- mysql> explain select user_id from t_order group by user_id;
- +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
- | 1 | SIMPLE | t_order | range | NULL | user_id | 5 | NULL | 3 | Using index for group-by |
- +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
- 1 row in set (0.00 sec)
除了上面的三个说明,还须要注意rows的数值,多行之间的数值是乘积的关系,能够估算大概要处理的行数,若是乘积很大,那就颇有优化的必要了。
mysql中有一个explain 命令能够用来分析select 语句的运行效果,例如explain能够得到select语句
使用的索引状况、排序的状况等等。除此之外,explain 的extended 扩展可以在本来explain的基础
上额外的提供一些查询优化的信息,这些信息能够经过mysql的show warnings命令获得。下面是一个最简单的例子。
首先执行对想要分析的语句进行explain,并带上extended选项
mysql> explain extended select * from account;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: account
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1
filtered: 100.00
Extra:
1 row in set, 1 warning (0.00 sec)
接下来再执行Show Warnings
mysql> show warnings;
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: select `dbunit`.`account`.`id` AS `id`,`dbunit`.`account`.`name` AS `name` from `dbunit`.`account`
1 row in set (0.00 sec)
从 show warnings的输出结果中咱们能够看到本来的select * 被mysql优化成了
select `dbunit`.`account`.`id` AS `id`,`dbunit`.`account`.`name` AS `name`。
explain extended 除了可以告诉咱们mysql的查询优化能作什么,同时也能告诉咱们mysql的
查询优化作不了什么。Mysql performance的Extended EXPLAIN这篇文中中做者就利用explain
extended +show warnings 找到了mysql查询优化器中不能查询优化的地方。
从 EXPLAIN extended SELECT * FROM sbtest WHERE id>5 AND id>6 AND c="a" AND pad=c
语句的输出咱们得知mysql的查询优化器不能将id>5 和 id>6 这两个查询条件优化合并成一个 id>6。
在mysql performance的explain extended文章中第三个例子和静室的explain的extended选项文章中,
两位做者也对explain extended作了进一步的实验,从这个两篇文中中咱们能够得出结论是从
explain extend的输出中,咱们能够看到sql的执行方式,对于分析sql仍是颇有帮助的。
下面特别摘抄了静室的explain的extended选项这篇文章中的内容
/******************************如下代码和分析摘抄至静室的explain的extended选项**************/
mysql>explain extended select * from t where a in (select b from i);
+----+--------------------+-------+------+
| id | select_type | table | type |
+----+--------------------+-------+------+
| 1 | PRIMARY | t | ALL |
| 2 | DEPENDENT SUBQUERY | i | ALL |
+----+--------------------+-------+------+
2 rows in set, 1 warning (0.01 sec)
子查询看起来和外部的查询没有任何关系,为何MySQL显示的是DEPENDENT SUBQUERY,
和外部相关的查询呢?从explain extended的结果咱们就能够看出缘由了。
mysql>show warnings
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: select `test`.`t`.`a` AS `a`,`test`.`t`.`b` AS `b`,`test`.`t`.`c` AS `c`
from `test`.`t` where
(`test`.`t`.`a`,
(select 1 AS `Not_used` from `test`.`i`
where ((`test`.`t`.`a`) = `test`.`i`.`b`)))
1 row in set (0.00 sec)
在这里MySQL改写了SQL,作了in的优化。
/******************************以上代码和分析摘抄至静室的explain的extended选项*********************/
不过须要注意的一点是从EXPLAIN extended +show warnings获得“优化之后”的查询语句
可能还不是最终优化执行的sql,或者说explain extended看到的信息还不足以说明mysql最
终对查询语句优化的结果。一样仍是mysql formance的explain Extended这篇文章的第二个
例子就说明了这种状况
/*****************************************************************************************************/
mysql> EXPLAIN extended SELECT t1.id,t2.pad FROM sbtest t1, sbtest t2 WHERE t1.id=5
AND t2.k=t1.k;
+----+-------------+-------+-------+---------------+---------+---------+-------+-------+-------+
| id | select_type | TABLE | type | possible_keys | KEY | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+-------+-------+
| 1 | SIMPLE | t1 | const | PRIMARY,k | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | t2 | ref | k | k | 4 | const | 55561 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+-------+-------+
2 rows IN SET, 1 warning (0.00 sec)
mysql> SHOW warnings
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: SELECT `test`.`t1`.`id` AS `id`,`test`.`t2`.`pad` AS `pad` FROM `test`.`sbtest` `t1`
JOIN `test`.`sbtest` `t2` WHERE ((`test`.`t2`.`k` = `test`.`t1`.`k`) AND (`test`.`t1`.`id` = 5))
1 row IN SET (0.00 sec)
/*************************************************************************************************/
从Explain的结果中咱们能够获得t1表的查询使用的是"const"类型,也就是说mysql查询的时候
会先由t1.id=5 找到t1.k 再利用t1.k的值去t2表中查询数据,很显然这样的查询优化结果没有在
接下来的Show Warings输出中找到。
sql执行顺序
(1)from
(3) join
(2) on
(4) where
(5)group by(开始使用select中的别名,后面的语句中均可以使用)
(6) avg,sum....
(7)having
(8) select
(9) distinct
(10) order by
从这个顺序中咱们不难发现,全部的 查询语句都是从from开始执行的,在执行过程当中,每一个步骤都会为下一个步骤生成一个虚拟表,这个虚拟表将做为下一个执行步骤的输入。
第一步:首先对from子句中的前两个表执行一个笛卡尔乘积,此时生成虚拟表 vt1(选择相对小的表作基础表)
第二步:接下来即是应用on筛选器,on 中的逻辑表达式将应用到 vt1 中的各个行,筛选出知足on逻辑表达式的行,生成虚拟表 vt2
第三步:若是是outer join 那么这一步就将添加外部行,left outer jion 就把左表在第二步中过滤的添加进来,若是是right outer join 那么就将右表在第二步中过滤掉的行添加进来,这样生成虚拟表 vt3
第四步:若是 from 子句中的表数目多余两个表,那么就将vt3和第三个表链接从而计算笛卡尔乘积,生成虚拟表,该过程就是一个重复1-3的步骤,最终获得一个新的虚拟表 vt3。
第五步:应用where筛选器,对上一步生产的虚拟表引用where筛选器,生成虚拟表vt4,在这有个比较重要的细节不得不说一下,对于包含outer join子句的查询,就有一个让人感到困惑的问题,到底在on筛选器仍是用where筛选器指定逻辑表达式呢?on和where的最大区别在于,若是在on应用逻辑表达式那么在第三步outer join中还能够把移除的行再次添加回来,而where的移除的最终的。举个简单的例子,有一个学生表(班级,姓名)和一个成绩表(姓名,成绩),我如今须要返回一个x班级的全体同窗的成绩,可是这个班级有几个学生缺考,也就是说在成绩表中没有记录。为了获得咱们预期的结果咱们就须要在on子句指定学生和成绩表的关系(学生.姓名=成绩.姓名)那么咱们是否发如今执行第二步的时候,对于没有参加考试的学生记录就不会出如今vt2中,由于他们被on的逻辑表达式过滤掉了,可是咱们用left outer join就能够把左表(学生)中没有参加考试的学生找回来,由于咱们想返回的是x班级的全部学生,若是在on中应用学生.班级='x'的话,left outer join会把x班级的全部学生记录找回(感谢网友康钦谋__康钦苗的指正),因此只能在where筛选器中应用学生.班级='x' 由于它的过滤是最终的。
第六步:group by 子句将中的惟一的值组合成为一组,获得虚拟表vt5。若是应用了group by,那么后面的全部步骤都只能获得的vt5的列或者是聚合函数(count、sum、avg等)。缘由在于最终的结果集中只为每一个组包含一行。这一点请牢记。
第七步:应用cube或者rollup选项,为vt5生成超组,生成vt6.
第八步:应用having筛选器,生成vt7。having筛选器是第一个也是为惟一一个应用到已分组数据的筛选器。
第九步:处理select子句。将vt7中的在select中出现的列筛选出来。生成vt8.
第十步:应用distinct子句,vt8中移除相同的行,生成vt9。事实上若是应用了group by子句那么distinct是多余的,缘由一样在于,分组的时候是将列中惟一的值分红一组,同时只为每一组返回一行记录,那么因此的记录都将是不相同的。
第十一步:应用order by子句。按照order_by_condition排序vt9,此时返回的一个游标,而不是虚拟表。sql是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是可有可无的。对表进行排序的查询能够返回一个对象,这个对象包含特定的物理顺序的逻辑组织。这个对象就叫游标。正由于返回值是游标,那么使用order by 子句查询不能应用于表表达式。排序是很须要成本的,除非你必需要排序,不然最好不要指定order by,最后,在这一步中是第一个也是惟一一个可使用select列表中别名的步骤。
第十二步:应用top选项。此时才返回结果给请求者即用户。
转自:http://www.javashuo.com/article/p-nbksvujx-g.html