where 子句将用来限制哪些记录匹配了下一个表或者发送给客户端。除非你特别地想要取得或者检查表种的所有记录,否则的话当查询的 extra 字段值不是 using where 并且表连接类型是 all 或 index 时可能表示有问题。
如果你想要让查询尽可能的快,那么就应该注意 extra 字段的值为using filesort 和 using temporary 的情况。
你可以通过 explain 的结果中 rows 字段的值的乘积大概地知道本次连接表现如何。它可以粗略地告诉我们mysql在查询过程中会查询多少条记录。如果是使用系统变量 max_join_size 来取得查询结果,这个乘积还可以用来确定会执行哪些多表 select 语句。详情请看7.5.2 tuning server parameters。
下面的例子展示了如何通过 explain 提供的信息来较大程度地优化多表联合查询的性能。
假设有下面的 select 语句,正打算用 explain 来检测:
explain select tt.ticketnumber, tt.timein,
tt.projectreference, tt.estimatedshipdate,
tt.actualshipdate, tt.clientid,
tt.servicecodes, tt.repetitiveid,
tt.currentprocess, tt.currentdpperson,
tt.recordvolume, tt.dpprinted, et.country,
et_1.country, do.custname
from tt, et, et as et_1, do
where tt.submittime is null
and tt.actualpc = et.employid
and tt.assignedpc = et_1.employid
and tt.clientid = do.custnmbr;
在这个例子中,先做以下假设:
要比较的字段定义如下:
table column column type
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 primary null null null 74
do all primary null null null 2135
et_1 all primary 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 all assignedpc, null null null 3872 using
clientid, where
actualpc
do all primary null null null 2135
range checked for each record (key map: 1)
et_1 all primary null null null 74
range checked for each record (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 的结果如下:
