一、ID列
1.单纯的join(没有子查询) ID都是1,且从上到下。
desc select count(*) from employees d
join salaries t on d.emp_no=t.emp_no
join employees s on d.emp_no=s.emp_no
where salary between 600 and 66961;
上述SQL的执行顺序为:首先运行d,再运行s,最后运行t。
MySQL看执行计划都是从上往下看。
2.subquery(子查询),scala subquery(标量子查询)都会使ID递增。
desc select count(*),(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) b
,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) c
from employees d
join salaries t on d.emp_no=t.emp_no
join employees s on d.emp_no=s.emp_no
where t.salary between 6000 and 66961;
二、select_type 列
1.simple
不需要union 操作或者没有子查询的都是simple。
最外层SELECT查询的select_type通常为SIMPLE。
2.primary
用union 操作或者有子查询的都是primary 。
desc select count(*),(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) b
,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) c
from (select * from employees limit 100) s;
3.UNION
由UNION操作联合而成的单位SELECT查询中,除第一个外,第二个以后的所有单位SELECT查询的select_type都是是UNION。
UNION的第一个单位SELECT的select_type(下面的UNION RESULT)不是UNION,而是DERIVED,它是一个去掉重复值的临时表,用于存储UNION后的查询结果。
desc select * from dept_emp a where dept_no='d003'
union
select * from dept_emp b where dept_no='d004'
union
select * from dept_emp c where dept_no='d005';
desc select * from dept_emp a where dept_no='d003'
union all
select * from dept_emp b where dept_no='d004'
union all
select * from dept_emp c where dept_no='d005';
union all不出现union result是因为不去重。
4.subquery
这里的subquery仅仅指除from子句以外使用的子查询。注意点是跟外部表没有关联。
desc select count(*) from employees d
join salaries t on d.emp_no=t.emp_no
join employees s on d.emp_no=s.emp_no
where salary between 6000 and 66961 ;
执行计划没有错误,不代表SQL执行没错误。
desc
select count(1)
,(select d.emp_no from employees d
join salaries t on d.emp_no=t.emp_no
where salary between 6000 and 66961 ) a
from t_group t limit 1
ERROR 1242 (21000): Subquery returns more than 1 row
5.dependent subquery
必须依附于外面的值,如scala subquery或者exists。
由于dependent subquery先执行外部查询,再执行内部查询(子查询),所以它比一般子查询(不带dependent 关键字)的处理速度要慢。
5.1. scala subquery
desc select count(*) ,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) b
,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) c
from employees s;
5.2 exists
desc select count(*)
from employees s
where exists (select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1)
6.derived
(1)from后面表的位置上的subquery。
(2)derived是生成在内存或者临时表空间中的。
(3)如果derived当驱动表的时候,要点是要减少数据量为目的。
(4)当作被驱动表的时候,产生auto_key,也是要减少数据量为目的。
(5)5.7当中,optimizer_swith='derived_merge=on'可以把简单subquery打开成join。
7.materialized
三、table列
1.null表示不使用任何表。
2.表名或者有别名。
3.<derved + id> <union + id> 表示是临时表<> 里的数字是id列。
四、Type列
1.const
使用primary key或者uniquer取得一条数据。
特点是 show warnings\G可以显示常数。
这说明了MySQL执行计划是事先运行一部分。
desc select * from employees where emp_no=10001;
2.eq_ref
(1)必须是join。
(2)且满足被驱动表的连接条件uniquer key或者primary key。
(3)如果是复合主键或者unique key相应的列名必须都要包含。
desc select * from dept_emp d join employees e on d.emp_no=e.emp_no where d.dept_no='d005';
3.ref
对索引列,进行 = (“等于”)条件。
desc select * from dept_emp d where d.dept_no='d005';
4.range
索引范围查询,主要出现在 >,< ,between,in,like等。
desc select * from employees d where d.emp_no>10001;
5.index
索引全扫描,比起表全扫描且order by的情况下快。
但是绝大部分情况下是优化对象。
主要用于:
(1)不能使用range、const、ref。
(2)只查询索引列,即不回表。
(3)使用索引进行排序或者聚合即省略排序。
满足联合索引中前导列没有在where条件且查询列在索引或者primary key中包含emp_no
desc select emp_no from salaries where from_date='1986-06-26';
在聚合运算中,group by后面的列在索引或者primary key中,且查询列也是在索引中。
desc select from_date,count(*) from salaries group by from_date;
desc select emp_no,from_date,count(*) from salaries group by emp_no,from_date;
6.all
全表扫描。
一般情况下是非常不好的执行计划,但是在进行大表中查询超过一半以上的值的时候效果会更好。
导致全表扫描的情况较多:没有索引,对索引进行加工,索引列进行了隐藏类型转换,对日期类型进行like 20%,单列索引的时候,对数字列进行 like 30%。
单列索引,对索引列进行加工。
desc select * from employees where emp_no +1=10001;
索引列进行了隐藏类型转换
desc select * from test1 where id =10;
对日期类型进行like 20%
对数字列进行like 30%
desc select * from employees where emp_no like '10001%'
五、possible_keys
列出表所在的索引名称,对优化没有什么帮助,请无视他。
六、key
实际使用的索引名称。
key中列出索引名称。
show index from tab中查询此索引都有哪些列。
七、key_len
在复合索引中可以看出索引中到底使用了哪些索引。
下面的例子中可以看出key_len是不一样的。
desc select * from salaries where emp_no='10001' and from_date like '1986%';
desc select * from salaries where emp_no='10001' and from_date='1986-06-26';
第一个例子like '1986%' 中说明 from_date的索引没有用到,只用到了emp_no,所以是4 。第二个例子 ='1986-06-26',说明用到了emp_no和from_date索引,所以是4+3=7 。
八、Rows
此列是MySQL优化器根据统计信息预估出来的值,不准确。
九、filtered
跟rows一样是预估值,ritered非100的情况是extra有using where关键字,表示从innodb引擎中拿到数据后再加工的比率。
十、Extra
1.distinct
MySQL在join过程中d中取出一行之后,查询de表的时候,碰到一行就停止,有点像exists。
(1)条件时必须是join。
(2)distinct的是驱动表的主键且只能是单列。
(3)被驱动表必须不能回表,且跟驱动表是1:M关系。
2.select tables optimized away
复合主键(两个)的时候,使用其中的任何一个只能用 = 号,求出另一个且不能保护group by就是在计算一个索引的上下两列一行。
select当中只有min,max,count的时候出现。
3.using filesort
在进行order by,group by且没有使用索引的时候。
4.using index
只使用索引,不回表就可以查到。所有的数据都在数据中就可以取出来。
5.using temporary
MySQL执行过程中为了存储中间结果会使用 temporary table。
如果执行计划中出现using temporary即使使用 temporary table,但是还是无法判断实在内存中生成还是在disk中生成。
(1)order by,group by没有使用索引的时候。
(2)执行计划中的select_type为derved。
6.using where
一般using where跟filtered ,rows一起看。
using where 表示从存储引擎中拿到一些数据,然后再过滤。
其中rows是在存储引擎中拿数据预算值,filtered是再过滤的百分百。
下面的rows 表示key primary const模式中有17行数据从innodb中导入到MySQL Server内存中,其中在内存中过滤掉了11%数据。简单来说就是emp_no=‘10001’ 数据通过primary 索引。把17行数据倒到内存中然后通过from_date like ‘1986%’ fiter之后最终17*11%的数据给用户。
几个例子:
Eg:1
desc select t1.emp_no from t1 join (
select distinct a.emp_no from (select e.emp_no from employees e where e.emp_no between 10001 and 11000 limit 1000) a
) E1 on t1.emp_no = E1.emp_no limit 5 ;