在 explain的帮助下,您就知道什么时候该给表添加索引,以使用索引来查找记录从而让select 运行更快。
如果由于不恰当使用索引而引起一些问题的话,可以运行 analyze table来更新该表的统计信息,例如键的基数,它能帮您在优化方面做出更好的选择。
explain 返回了一行记录,它包括了select语句中用到的各个表的信息。这些表在结果中按照mysql即将执行的查询中读取的顺序列出来。mysql用一次扫描多次连接(single-sweep,multi-join)的方法来解决连接。这意味着mysql从第一个表中读取一条记录,然后在第二个表中查找到对应的记录,然后在第三个表中查找,依次类推。当所有的表都扫描完了,它输出选择的字段并且回溯所有的表,直到找不到为止,因为有的表中可能有多条匹配的记录下一条记录将从该表读取,再从下一个表开始继续处理。
在mysql version 4.1中,explain输出的结果格式改变了,使得它更适合例如 union语句、子查询以及派生表的结构。更令人注意的是,它新增了2个字段: id和 select_type。当你使用早于mysql4.1的版本就看不到这些字段了。
explain结果的每行记录显示了每个表的相关信息,每行记录都包含以下几个字段:
id
本次 select 的标识符。在查询中每个 select都有一个顺序的数值。
select_type
select 的类型,可能会有以下几种:
select_type: SELECT类型,有以下几种不同的类型
(1).SIMPLE: 简单的SELECT(不使用UNION或子查询)
(2).PRIMARY: 最外面的SELECT,如果我们使用UNION或子查询,第一个查询将会是这个类型
(3).UNION: 使用UNION查询时,除第一个语句外的所有语句会返回这个类型
(4).DEPENDENT UNION:UNION中的第二个或后面的SELECT语句,取决于外面的查询。
(5).UNION RESULT: UNION的结果。
(6).SUBQUERY: 子查询中的第一个SELECT。
(7).DEPENDENT SUBQUERY: 子查询中的第一个SELECT,取决于外面的查询。
(8).DERIVED: 衍生表会返回这个类型。如:select * from (select * from jos_content) as A;。
table
记录查询引用的表。
type: 表示表的连接类型,性能由好到差的类型类型为
(System(表中仅有一行,即常量表),
const(单表中最多有一个匹配行),
eq_ref(对于前面的每一行,在此表中只查询一条记录),
ref(使用普通的索引),
ref_or_null(和ref类似,但是条件中包含对于NULL查询),
index_merge(索引合并优化),
unique_subquery(in的后面是一个查询主键字段的子查询),
index_subquery(类似unique_subquery,主要是in的后面是查询非唯一索引字段的子查询),
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 是指可以应用到该表的索引,如果为NULL则没有。
key 是指用到的索引。
key_len 是索引的长度,在不影响查询精度的情况下,值越小越好。
ref 是指索引的那一列被使用了。一般会是个常数。
rows MYSQL认为必须检查的用来返回请求数据的行数。
extra 是指额外的信息。也是比较重要的。
但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢
type
表连接类型。以下列出了各种不同类型的表连接,依次是从最好的到最差的:
system>const>eq_ref>ref>ref_or_null>unique_subquery>index_subquery>range>index>all
system:表只有一行记录(等于系统表)。这是 const表连接类型的一个特例。
const:表中最多只有一行匹配的记录,它在查询一开始的时候就会被读取出来。由于只有一行记录,在余下的优化程序里该行记录的字段值可以被当作是一个恒定值。const表查询起来非常快,因为只要读取一次!const 用于在和 primary key 或unique索引中有固定值比较的情形。【普通索引虽然也是唯一值返回的是type = ref, ref=const】下面的几个查询中,tbl_name 就是 c表了:
select * from tbl_name where primary_key=1; select * from tbl_namewhere primary_key_part1=1 and primary_key_part2=2;
eq_ref:从该表中会有一行记录被读取出来以和从前一个表中读取出来的记录做联合。与const类型不同的是,这是最好的连接类型。它用在索引所有部分都用于做连接并且这个索引是一个primary key 或 unique类型。eq_ref可以用于在进行"="做比较时检索字段。比较的值可以是固定值或者是表达式,表达示中可以使用表里的字段,它们在读表之前已经准备好了。以下的几个例子中,mysql使用了eq_ref 连接来处理 ref_table:
select * from ref_table,other_tablewhereref_table.key_column=other_table.column; select *fromref_table,other_tablewhereref_table.key_column_part1=other_table.columnandref_table.key_column_part2=1;
ref: 该表中所有符合检索值的记录都会被取出来和从上一个表中取出来的记录作联合。ref用于连接程序使用键的最左前缀或者是该键不是primary key 或unique索引(换句话说,就是连接程序无法根据键值只取得一条记录)的情况。当根据键值只查询到少数几条匹配的记录时,这就是一个不错的连接类型。ref还可以用于检索字段使用 =操作符来比较的时候。以下的几个例子中,mysql将使用 ref 来处理ref_table:
select * from ref_table where key_column=expr; select *fromref_table,other_table whereref_table.key_column=other_table.column;select * fromref_table,other_tablewhereref_table.key_column_part1=other_table.columnandref_table.key_column_part2=1;
ref_or_null: 这种连接类型类似 ref,不同的是mysql会在检索的时候额外的搜索包含null值的记录。这种连接类型的优化是从mysql4.1.1开始的,它经常用于子查询。在以下的例子中,mysql使用ref_or_null 类型来处理ref_table:
select * from ref_table where key_column=expr or key_column is null;
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:只有在给定范围的记录才会被取出来,利用索引来取得一条记录。key字段表示使用了哪个索引。key_len字段包括了使用的键的最长部分。这种类型时ref 字段值是 null。
range用于将某个字段和一个定植用以下任何操作符比较时 =, <>, >,>=,<, <=, is null, <=>, between, 或 in:
select * from tbl_name where key_column = 10;
select * fromtbl_namewhere key_column between 10 and 20;
select * from tbl_namewherekey_column in (10,20,30);
select * from tbl_name wherekey_part1= 10 andkey_part2 in (10,20,30);
explain select productno from price_relation_tmp where productno like '18%'; productno普通索引
index: 连接类型跟 all 一样,不同的是它只扫描索引树。它通常会比 all快点,因为索引文件通常比数据文件小。mysql在查询的字段知识单独的索引的一部分的情况下使用这种连接类型。
explain select productno from price_relation_tmp where productno like '%18%'; productno 普通索引
all: 将对该表做全部扫描以和从前一个表中取得的记录作联合。这时候如果第一个表没有被标识为const的话就不大好了,在其他情况下通常是非常糟糕的。正常地,可以通过增加索引使得能从表中更快的取得记录以避免all。
explain select * from price_relation_tmp where productno='344257-061';
字段没有做任何索引尽管使用"="
possible_keys
possible_keys字段是指 mysql在搜索表记录时可能使用哪个索引。注意,这个字段完全独立于explain 显示的表顺序。这就意味着possible_keys里面所包含的索引可能在实际的使用中没用到。如果这个字段的值是null,就表示没有索引被用到。这种情况下,就可以检查where子句中哪些字段那些字段适合增加索引以提高查询的性能。就这样,创建一下索引,然后再用explain检查一下。详细的查看章节"14.2.2 alter tablesyntax"。想看表都有什么索引,可以通过 show index fromtbl_name来看。
explain返回的字段含义说明
key
key字段显示了mysql实际上要用的索引。当没有任何索引被用到的时候,这个字段的值就是null。想要让mysql强行使用或者忽略在possible_keys字段中的索引列表,可以在查询语句中使用关键字force index, use index,或 ignoreindex。如果是 myisam 和 bdb 类型表,可以使用 analyzetable 来帮助分析使用使用哪个索引更好。如果是myisam类型表,运行命令 myisamchk --analyze也是一样的效果。详细的可以查看章节"14.5.2.1 analyzetablesyntax"和"5.7.2 table maintenance and crash recovery"。
key_len
key_len 字段显示了mysql使用索引的长度。当 key 字段的值为 null时,索引的长度就是 null。注意,key_len的值可以告诉你在联合索引中mysql会真正使用了哪些索引。
ref
ref 字段显示了哪些字段或者常量被用来和 key配合从表中查询记录出来。
rows
rows 字段显示了mysql认为在查询中应该检索的记录数。
extra
本字段显示了查询中mysql的附加信息。以下是这个字段的几个不同值的解释:
distinct:mysql当找到当前记录的匹配联合结果的第一条记录之后,就不再搜索其他记录了。
not exists:mysql在查询时做一个 left join优化时,当它在当前表中找到了和前一条记录符合 left join条件后,就不再搜索更多的记录了。下面是一个这种类型的查询例子:
select * from t1 left join t2 on t1.id=t2.id where t2.id isnull;
假使 t2.id 定义为 not null。这种情况下,mysql将会扫描表 t1并且用 t1.id 的值在 t2 中查找记录。当在t2中找到一条匹配的记录时,这就意味着 t2.id 肯定不会都是null,就不会再在 t2 中查找相同 id值的其他记录了。也可以这么说,对于t1 中的每个记录,mysql只需要在t2 中做一次查找,而不管在 t2 中实际有多少匹配的记录。
range checked for each record (index map: #)
mysql没找到合适的可用的索引。取代的办法是,对于前一个表的每一个行连接,它会做一个检验以决定该使用哪个索引(如果有的话),并且使用这个索引来从表里取得记录。这个过程不会很快,但总比没有任何索引时做表连接来得快。
using filesort: mysql需要额外的做一遍从而以排好的顺序取得记录。排序程序根据连接的类型遍历所有的记录,并且将所有符合where条件的记录的要排序的键和指向记录的指针存储起来。这些键已经排完序了,对应的记录也会按照排好的顺序取出来。详情请看"7.2.9howmysql optimizes order by"。
using index
字段的信息直接从索引树中的信息取得,而不再去扫描实际的记录。这种策略用于查询时的字段是一个独立索引的一部分。
using temporary: mysql需要创建临时表存储结果以完成查询。这种情况通常发生在查询时包含了groupby 和 order by 子句,它以不同的方式列出了各个字段。
using where
where子句将用来限制哪些记录匹配了下一个表或者发送给客户端。除非你特别地想要取得或者检查表种的所有记录,否则的话当查询的extra 字段值不是 using where 并且表连接类型是 all 或 index时可能表示有问题。
如果你想要让查询尽可能的快,那么就应该注意 extra 字段的值为usingfilesort 和 using temporary 的情况。
你可以通过 explain 的结果中rows字段的值的乘积大概地知道本次连接表现如何。它可以粗略地告诉我们mysql在查询过程中会查询多少条记录。如果是使用系统变量max_join_size 来取得查询结果,这个乘积还可以用来确定会执行哪些多表select 语句。
下面的例子展示了如何通过 explain提供的信息来较大程度地优化多表联合查询的性能。
假设有下面的 select 语句,正打算用 explain 来检测:
explain select tt.ticketnumber, tt.timein,tt.projectreference,tt.estimatedshipdate, tt.actualshipdate,tt.clientid,tt.servicecodes, tt.repetitiveid,tt.currentprocess,tt.currentdppers tt.recordvolume, tt.dpprinted,et.country,et_1.country, do.custname from tt, et, et as et_1, dowherett.submittime is null and tt.actualpc = et.employidandtt.assignedpc = et_1.employid and tt.clientid = do.custnmbr;
在这个例子中,先做以下假设:
要比较的字段定义如下:
table column columntype
tt actualpc char(10)
tt assignedpc char(10)
tt clientid char(10)
et employid char(15)
do custnmbr char(15)
数据表的索引如下:
table index
tt actualpc
tt assignedpc
tt clientid
et employid (primary key)
do custnmbr (primary key)
tt.actualpc 的值是不均匀分布的。
在任何优化措施未采取之前,经过 explain分析的结果显示如下:
table type possible_keys key key_len ref rows extra
et all primarynull null null 74
do all primary null null null 2135
et_1 allprimary null null null 74
tt all assignedpc, null null null 3872 clientid, actualpc range checked for each record (key map: 35)
由于字段 type的对于每个表值都是all,这个结果意味着mysql对所有的表做一个迪卡尔积;这就是说,每条记录的组合。这将需要花很长的时间,因为需要扫描每个表总记录数乘积的总和。在这情况下,它的积是74 * 2135 * 74 * 3872 =45,268,558,720条记录。如果数据表更大的话,你可以想象一下需要多长的时间。
在这里有个问题是当字段定义一样的时候,mysql就可以在这些字段上更快的是用索引(对isam类型的表来说,除非字段定义完全一样,否则不会使用索引)。在这个前提下,varchar和 char是一样的除非它们定义的长度不一致。由于 tt.actualpc定义为char(10),et.employid 定义为 char(15),二者长度不一致。
为了解决这个问题,需要用 alter table 来加大 actualpc的长度从10到15个字符:
mysql> alter table tt modify actualpc varchar(15);
现在 tt.actualpc 和 et.employid 都是 varchar(15)
了。再来执行一次 explain 语句看看结果:
table type possible_keys key key_len ref rows extra
tt allassignedpc, null null null 3872 using clientid, where actualpc
do all primary null null null 2135 range checked for each record (keymap: 1)
et_1 all primary null null null 74 range checked for eachrecord (key map: 1) et eq_ref primary primary 15 tt.actualpc 1
这还不够,它还可以做的更好:现在 rows值乘积已经少了74倍。这次查询需要用2秒钟。
第二个改变是消除在比较 tt.assignedpc = et_1.employid 和 tt.clientid= do.custnmbr 中字段的长度不一致问题:
mysql> alter table tt modify assignedpc varchar(15), ->modify clientid varchar(15);
现在 explain 的结果如下:
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
这看起来已经是能做的最好的结果了。
遗留下来的问题是,mysql默认地认为字段 tt.actualpc的值是均匀分布的,然而表 tt并非如此。幸好,我们可以很方便的让mysql分析索引的分布:
mysql> analyze table tt;
到此为止,表连接已经优化的很完美了,explain 的结果如下:
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
请注意,explain 结果中的rows字段的值也是mysql的连接优化程序大致猜测的,请检查这个值跟真实值是否基本一致。如果不是,可以通过在select 语句中使用straight_join 来取得更好的性能,同时可以试着在from分句中用不同的次序列出各个表。
一.语法
explain < table_name >
例如: explain select * from t3 where id=3952602;
二.explain输出解释
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
1.id
我的理解是SQL执行的顺利的标识,SQL从大到小的执行.
例如:
mysql> explain select * from (select * from ( select * from t3 where id=3952602) a) b;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
| 3 | DERIVED | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | | 1 | |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
很显然这条SQL是从里向外的执行,就是从id=3 向上执行.
2. select_type
就是select类型,可以有以下几种
(1) SIMPLE
简单SELECT(不使用UNION或子查询等) 例如:
mysql> explain select * from t3 where id=3952602;
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
(2). PRIMARY
我的理解是最外层的select.例如:
mysql> explain select * from (select * from t3 where id=3952602) 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 | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | | 1 | |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
(3).UNION
UNION中的第二个或后面的SELECT语句.例如
mysql> explain select * from t3 where id=3952602 union all select * from t3 ;
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
| 1 | PRIMARY | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | |
| 2 | UNION | t3 | ALL | NULL | NULL | NULL | NULL | 1000 | |
|NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
(4).DEPENDENT UNION
UNION中的第二个或后面的SELECT语句,取决于外面的查询
mysql> explain select * from t3 where id in (select id from t3 where id=3952602 union all select id from t3) ;
+----+--------------------+------------+--------+-------------------+---------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+--------+-------------------+---------+---------+-------+------+--------------------------+
| 1 | PRIMARY | t3 | ALL | NULL | NULL | NULL | NULL | 1000 | Using where |
| 2 | DEPENDENT SUBQUERY | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | Using index |
| 3 | DEPENDENT UNION | t3 | eq_ref | PRIMARY,idx_t3_id | PRIMARY | 4 | func | 1 | Using where; Using index |
|NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------------+------------+--------+-------------------+---------+---------+-------+------+--------------------------+
(4).UNION RESULT
UNION的结果。
mysql> explain select * from t3 where id=3952602 union all select * from t3 ;
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
| 1 | PRIMARY | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | |
| 2 | UNION | t3 | ALL | NULL | NULL | NULL | NULL | 1000 | |
|NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+------------+-------+-------------------+---------+---------+-------+------+-------+
(5).SUBQUERY
子查询中的第一个SELECT.
mysql> explain select * from t3 where id = (select id from t3 where id=3952602 ) ;
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------------+
| 1 | PRIMARY | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | |
| 2 | SUBQUERY | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | | 1 | Using index |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------------+
(6). DEPENDENT SUBQUERY
子查询中的第一个SELECT,取决于外面的查询
mysql> explain select id from t3 where id in (select id from t3 where id=3952602 ) ;
+----+--------------------+-------+-------+-------------------+---------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+-------------------+---------+---------+-------+------+--------------------------+
| 1 | PRIMARY | t3 | index | NULL | PRIMARY | 4 | NULL | 1000 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | Using index |
+----+--------------------+-------+-------+-------------------+---------+---------+-------+------+--------------------------+
(7).DERIVED
派生表的SELECT(FROM子句的子查询)
mysql> explain select * from (select * from t3 where id=3952602) 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 | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | | 1 | |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
3.table
显示这一行的数据是关于哪张表的.
有时不是真实的表名字,看到的是derivedx(x是个数字,我的理解是第几步执行的结果)
mysql> explain select * from (select * from ( select * from t3 where id=3952602) a) b;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
| 3 | DERIVED | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | | 1 | |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
4.type
这列很重要,显示了连接使用了哪种类别,有无使用索引.
从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
(1).system
这是const联接类型的一个特例。表仅有一行满足条件.如下(t3表上的id是 primary key)
mysql> explain select * from (select * from t3 where id=3952602) 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 | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | | 1 | |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
(2).const
表最多有一个匹配行,它将在查询开始时被读取。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次!
const用于用常数值比较PRIMARY KEY或UNIQUE索引的所有部分时。在下面的查询中,tbl_name可以用于const表:
SELECT * from tbl_name WHERE primary_key=1;
SELECT * from tbl_name WHERE primary_key_part1=1和 primary_key_part2=2;
例如:
mysql> explain select * from t3 where id=3952602;
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
(3). eq_ref
对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY KEY。
eq_ref可以用于使用= 操作符比较的带索引的列。比较值可以为常量或一个使用在该表前面所读取的表的列的表达式。
在下面的例子中,MySQL可以使用eq_ref联接来处理ref_tables:
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;
例如
mysql> create unique index idx_t3_id on t3(id) ;
Query OK, 1000 rows affected (0.03 sec)
Records: 1000 Duplicates: 0 Warnings: 0
mysql> explain select * from t3,t4 where t3.id=t4.accountid;
+----+-------------+-------+--------+-------------------+-----------+---------+----------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------+-----------+---------+----------------------+------+-------+
| 1 | SIMPLE | t4 | ALL | NULL | NULL | NULL | NULL | 1000 | |
| 1 | SIMPLE | t3 | eq_ref | PRIMARY,idx_t3_id | idx_t3_id | 4 | dbatest.t4.accountid | 1 | |
+----+-------------+-------+--------+-------------------+-----------+---------+----------------------+------+-------+
(4).ref
对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或PRIMARY KEY(换句话说,如果联接不能基于关键字选择单个行的话),则使用ref。如果使用的键仅仅匹配少量行,该联接类型是不错的。
ref可以用于使用=或<=>操作符的带索引的列。
在下面的例子中,MySQL可以使用ref联接来处理ref_tables:
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;
例如:
mysql> drop index idx_t3_id on t3;
Query OK, 1000 rows affected (0.03 sec)
Records: 1000 Duplicates: 0 Warnings: 0
mysql> create index idx_t3_id on t3(id) ;
Query OK, 1000 rows affected (0.04 sec)
Records: 1000 Duplicates: 0 Warnings: 0
mysql> explain select * from t3,t4 where t3.id=t4.accountid;
+----+-------------+-------+------+-------------------+-----------+---------+----------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-------------------+-----------+---------+----------------------+------+-------+
| 1 | SIMPLE | t4 | ALL | NULL | NULL | NULL | NULL | 1000 | |
| 1 | SIMPLE | t3 | ref | PRIMARY,idx_t3_id | idx_t3_id | 4 | dbatest.t4.accountid | 1 | |
+----+-------------+-------+------+-------------------+-----------+---------+----------------------+------+-------+
2 rows in set (0.00 sec)
(5). ref_or_null
该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。在解决子查询中经常使用该联接类型的优化。
在下面的例子中,MySQL可以使用ref_or_null联接来处理ref_tables:
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;
(6). index_merge
该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。
例如:
mysql> explain select * from t4 where id=3952602 or accountid=31754306 ;
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
| 1 | SIMPLE | t4 | index_merge | idx_t4_id,idx_t4_accountid | idx_t4_id,idx_t4_accountid | 4,4 | NULL | 2 | Using union(idx_t4_id,idx_t4_accountid); Using where |
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
1 row in set (0.00 sec)
(7). unique_subquery
该类型替换了下面形式的IN子查询的ref:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。
(8).index_subquery
该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引:
value IN (SELECT key_column FROM single_table WHERE some_expr)
(9).range
只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。key_len包含所使用索引的最长关键元素。在该类型中ref列为NULL。
当使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比较关键字列时,可以使用range
mysql> explain select * from t3 where id=3952602 or id=3952603 ;
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
| 1 | SIMPLE | t3 | range | PRIMARY,idx_t3_id | idx_t3_id | 4 | NULL | 2 | Using where |
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
1 row in set (0.02 sec)
(10).index
该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。
当查询只使用作为单索引一部分的列时,MySQL可以使用该联接类型。
(11). ALL
对于每个来自于先前的表的行组合,进行完整的表扫描。如果表是第一个没标记const的表,这通常不好,并且通常在它情况下很差。通常可以增加更多的索引而不要使用ALL,使得行能基于前面的表中的常数值或列值被检索出。
5.possible_keys
possible_keys列指出MySQL能使用哪个索引在该表中找到行。注意,该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询
6. key
key列显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
7.key_len
key_len列显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。
使用的索引的长度。在不损失精确性的情况下,长度越短越好
8. ref
ref列显示使用哪个列或常数与key一起从表中选择行。
9. rows
rows列显示MySQL认为它执行查询时必须检查的行数。
10. Extra
该列包含MySQL解决查询的详细信息,下面详细.
(1).Distinct
一旦MYSQL找到了与行相联合匹配的行,就不再搜索了
(2).Not exists
MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,
就不再搜索了
(3).Range checked for each
Record(index map:#)
没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
(4).Using filesort
看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
(5).Using index
列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
(6).Using temporary
看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上
(7).Using where
使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题
优化GROUP BY语句
默认情况下,MySQL对所有GROUP BY col1,col2...的字段进行排序。这与在查询中指定ORDER BY col1,col2...类似。因此,如果显式包括一个包含相同的列的ORDER BY子句,则对MySQL的实际执行性能没有什么影响。 如果查询包括GROUP BY 但用户想要避免排序结果的消耗,则可以指定ORDER By NULL禁止排序,例如:
explain select id, sum(moneys) from sales2 group by id \G
explain select id, sum(moneys) from sales2 group by id order by null \G
你可以通过比较发现第一条语句会比第二句在Extra:里面多了Using filesort.而恰恰filesort是最耗时的。
EXPLAIN sql优化方法(2) Using temporary ; Using filesort
优化ORDER BY语句
在某些情况中,MySQL可以使用一个索引来满足ORDER BY子句,而不需要额外的排序。WHERE 条件和 ORDER BY使用相同的索引,并且ORDER BY的顺序和索引顺序相同,并且ORDER BY的字段都是升序或者都是降序。
例如:
SELECT * FROM t1 ORDER BY key_part1,key_part2,....:
SELECT * FROM t1 WHERE key_part1 = 1 ORDER BY key_part1 DESC,key_part2 DESC;
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;
但是以下的情况不使用索引:
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
--ORDER by的字段混合ASC 和 DESC
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
----用于查询行的关键字与ORDER BY 中所使用的不相同
SELECT * FROM t1 ORDER BY key1, key2;
----对不同的关键字使用ORDER BY
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . idleft join jos_sections C on A . sectionid = C . id order by B . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
| 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 46585 | Using temporary ;Using filesort |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | |
| 1 | SIMPLE | C | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . sectionid | 1 | Usingindex |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
3 rows in set ( 0.00 sec )
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . idleft join jos_sections C on A . sectionid = C . id order by A . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+
| 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 46585 | Using filesort |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | |
| 1 | SIMPLE | C | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . sectionid | 1 | Usingindex |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+
对于上面两条语句,只是修改了一下排序字段,而第一个使用了Using temporary,而第二个却没有。在日常的网站维护中,如果有Using temporary出现,说明需要做一些优化措施了。
而为什么第一个用了临时表,而第二个没有用呢?
因为如果有ORDER BY子句和一个不同的GROUP BY子句,或者如果ORDER BY或GROUP BY中的字段都来自其他的表而非连接顺序中的第一个表的话,就会创建一个临时表了。
那么,对于上面例子中的第一条语句,我们需要对jos_categories的id进行排序,可以将SQL做如下改动:
mysql > explain select B . id , B . title , A . title from jos_categories A left join jos_content B on A . id = B . catidleft join jos_sections C on B . sectionid = C . id order by A . id ;
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
| 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 18 | Using filesort |
| 1 | SIMPLE | B | ref | idx_catid | idx_catid | 4 | joomla_test . A . id | 3328 | |
| 1 | SIMPLE | C | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . B . sectionid | 1 | Usingindex |
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
3 rows in set ( 0.00 sec )
这样我们发现,不会再有Using temporary了,而且在查询jos_content时,查询的记录明显有了数量级的降低,这是因为jos_content的idx_catid起了作用。
所以结论是:
尽量对第一个表的索引键进行排序,这样效率是高的。
我们还会发现,在排序的语句中都出现了Using filesort,字面意思可能会被理解为:使用文件进行排序或中文件中进行排序。实际上这是不正确的,这是一个让人产生误解的词语。
当我们试图对一个没有索引的字段进行排序时,就是filesoft。它跟文件没有任何关系,实际上是内部的一个快速排序。
然而,当我们回过头来再看上面运行过的一个SQL的时候会有以下发现:
mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A .catid = B . id and A . sectionid = C . id order by C . id ;
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
| 1 | SIMPLE | C | index | PRIMARY | PRIMARY | 4 | NULL | 1 | Using index|
| 1 | SIMPLE | A | ref | idx_catid , idx_section | idx_section | 4 | joomla_test . C . id | 23293 |Using where |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Usingwhere |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
3 rows in set ( 0.00 sec )
这是我们刚才运行过的一条语句,只是加了一个排序,而这条语句中C表的主键对排序起了作用,我们会发现Using filesort没有了。
而尽管在上面的语句中也是对第一个表的主键进行排序,却没有得到想要的效果(第一个表的主键没有用到),这是为什么呢?实际上以上运行过的所有left join的语句中,第一个表的索引都没有用到,尽管对第一个表的主键进行了排序也无济于事。不免有些奇怪!
于是我们继续测试了下一条SQL:
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . idleft join jos_sections C on A . sectionid = C . id where A . id < 100 ;
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | A | range | PRIMARY | PRIMARY | 4 | NULL | 90 | Using where |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | |
| 1 | SIMPLE | C | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . sectionid | 1 | Usingindex |
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
3 rows in set ( 0.05 sec )
然后,当再次进行排序操作的时候,Using filesoft也没有再出现
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . idleft join jos_sections C on A . sectionid = C . id where A . id < 100 order by A . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | A | range | PRIMARY | PRIMARY | 4 | NULL | 105 | Using where |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | |
| 1 | SIMPLE | C | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . sectionid | 1 | Usingindex |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
3 rows in set ( 0.00 sec )
这个结果表明:对where条件里涉及到的字段,Mysql会使用索引进行搜索,而这个索引的使用也对排序的效率有很好的提升。
写了段程序测试了一下,分别让以下两个SQL语句执行200次:
1 select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . id left joinjos_sections C on A . sectionid = C . id
2 select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A . catid = B. id and A . sectionid = C . id
3 select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . id left joinjos_sections C on A . sectionid = C . id order by rand () limit 10
4 select A . id from jos_content A left join jos_categories B on B . id = A . catid left join jos_sections C onA . sectionid = C . id order by A . id
结果是第(1)条平均用时20s ,第(2)条平均用时44s ,第(3)条平均用时70s ,第(4)条平均用时2s 。而且假如我们用explain观察第(3)条语句的执行情况,会发现它创建了temporary表来进行排序。
综上所述,可以得出如下结论:
1. 对需要查询和排序的字段要加索引。
2. 在一定环境下,left join还是比普通连接查询效率要高,但是要尽量少地连接表,并且在做连接查询时注意观察索引是否起了作用。
3. 排序尽量对第一个表的索引字段进行,可以避免mysql创建临时表,这是非常耗资源的。
4. 对where条件里涉及到的字段,应适当地添加索引,这样会对排序操作有优化的作用。
5. 在做随机抽取数据的需求时,避免使用order by rand(),从上面的例子可以看出,这种是很浪费数据库资源的,在执行过程中用show processlist查看,会发现第(3)条有Copying to tmp table on disk。而对(3)和(4)的对比得知,如果要实现这个功能,最好另辟奚径,来减轻Mysql的压力。
6. 从第4点可以看出,如果说在分页时我们能先得到主键,再根据主键查询相关内容,也能得到查询的优化效果。通过国外《High Performance MySQL》专家组的测试可以看出,根据主键进行查询的类似“SELECT ... FROM... WHERE id = ...”的SQL语句(其中id为PRIMARYKEY),每秒钟能够处理10000次 以上的查询,而普通的SELECT查询每秒只能处理几十次到几百次 。涉及到分页的查询效率问题,网上的可用资源越来越多,查询功能也体现出了它的重要性。也便是sphinx、lucene这些第三方搜索引擎的用武之地了。
7. 在平时的作业中,可以打开Mysql的Slow queries功能,经常检查一下是哪些语句降低的Mysql的执行效率,并进行定期优化。
派生表和视图的性能
从MySQL 4.1开始,它已经支持派生表、联机视图或者基本的FROM从句的子查询。
这些特性之间彼此相关,但是它们之间的性能比较如何呢?
MySQL 5.0 中的派生表似乎和视图实现的方式不同,尽管我从合并的代码基数来看觉得在查询优化上应该是一样的。
派生表仍然以临时表的方式显式地处理,而且还是没有索引的临时表(因此最好不要像在例子中那样连接2个派生表)
需要考虑的另一方面是,派生表需要被显式处理,尽管只是执行 EXPLAIN 语句。因此如果在 FROM 字句中的 SELELCT 操作上犯了错误,例如忘记了写上连接的条件,那么 EXPLAIN 可能会一直在运行。
视图则不同,它无需被显式处理,只是把查询简单地重写了一下。只有在无法合并查询或者试图创建者请求时才需要被显式处理。
这意味着它们在性能上的差别如下:
在基本的表 上执行有索引 的查询,这非常快
1
mysql> SELECT * FROM test WHERE i=5 ;
+---+----------------------------------+
| i | j |
+---+----------------------------------+
| 5 | 0c88dedb358cd96c9069b73a57682a45 |
+---+----------------------------------+
1 row IN SET ( 0 .03 sec)
在派生表 上做同样 的查询,则如老牛拉破车
mysql> SELECT * FROM ( SELECT * FROM test) t WHERE i=5 ;
+---+----------------------------------+
| i | j |
+---+----------------------------------+
| 5 | 0c88dedb358cd96c9069b73a57682a45 |
+---+----------------------------------+
1 row IN SET ( 1 min 40 .86 sec)
在视图 上查询,又快起来了
mysql> CREATE VIEW v AS SELECT * FROM test;
Query OK, 0 rows affected ( 0 .08 sec)
mysql> SELECT * FROM v WHERE i=5 ;
+---+----------------------------------+
| i | j |
+---+----------------------------------+
| 5 | 0c88dedb358cd96c9069b73a57682a45 |
+---+----------------------------------+
1 row IN SET ( 0 .10 sec)
下面的2条EXPLAIN结果也许会让你很惊讶
mysql> EXPLAIN SELECT * FROM v WHERE i=5 ;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | TABLE | type | possible_keys | KEY | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1 | PRIMARY | test | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
1 row IN SET ( 0 .02 sec)
mysql> EXPLAIN SELECT * FROM ( SELECT * FROM test) t WHERE i=5 ;
+----+-------------+------------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | TABLE | type | possible_keys | KEY | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+---------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 1638400 | USING WHERE |
| 2 | DERIVED | test | ALL | NULL | NULL | NULL | NULL | 1638400 | |
+----+-------------+------------+------+---------------+------+---------+------+---------+-------------+
2 rows IN SET ( 54 .90 sec)
避免使用派生表 -- 如果可能,最好采用其他方式来编写查询语句,大部分情况都比派生表来的快。很多情况下,甚至连独立的临时表都来的快,因为可以适当增加索引。
可以考虑使用临时试图来取代派生表 如果确实需要在 FROM 子句中使用到子查询,可以考虑在查询时创建试图,当查询完之后删除试图。
不适合多表视图,多表时用派生表 取代 视图
explain select sum(pdm.qty) pre_total,pd.pre_doc_id from prepare_doc pd
left join pre_doc_item pdm on pd.pre_doc_id=pdm.pre_doc_id group by pd.pre_doc_id