1.union执行过程mysql
首先咱们建立一个表t1算法
create table t1(id int primary key, a int, b int, index(a)); delimiter ;; create procedure idata() begin declare i int; set i=1; while(i<=1000)do insert into t1 values(i, i, i); set i=i+1; end while; end;; delimiter ; call idata();
而后咱们执行一下这条语句sql
explain select 1000 as f union (select id from t1 order by id desc limit 2)
首先说下union的语义,union的语义是取两个结果的并集,重复的保留一行,而后咱们来看下explain的结果,第二行的key=PRIMARY,说明用到了主键索引。mysql优化
第三行的Extra的Using temporary说明用到了临时表优化
下面咱们看下这条语句的执行流程:spa
1.建立一个临时表,只有f一个字段,且为主键code
2.将1000这个数据插入临时表blog
3.子查询中步骤:排序
1.插入1000进入临时表,由于主键冲突,插入失败索引
2.插入第二行900,插入成功
4.将临时表数据做为结果返回,并删除临时表
这个过程的流程图以下:
若是咱们把union改为union all,就不须要使用临时表了,由于union all是重复的也保留,
你们能够看到extra这一列已经没有了Using temporary
explain select 1000 as f union all (select id from t1 order by id desc limit 2)
2.group by执行过程
咱们来看下面这条语句:
explain select id%10 as m, count(*) as c from t1 group by m;
能够看到explain结果
Using index(使用到了覆盖索引a,不须要回表); Using temporary(用到了临时表); Using filesort(对数据进行了排序)
这条语句的意思是将id%10进行分组统计,并按照m进行排序
执行流程以下:
1.建立临时表,增长m,c字段,m是主键
2.计算id%10的结果记为x
3.若是临时表里面没有主键为x的行,则插入(x,1),若是有的话,就将该行的c值加1
4.遍历完成后,按照m字段排序返回结果给客户端
流程图以下
接下来咱们看下这条语句的执行结果
explain select id%10 as m, count(*) as c from t1 group by m
其实,若是咱们不须要对查询结果进行排序,咱们能够加一个order by null
咱们执行一下这条语句
explain select id%10 as m, count(*) as c from t1 group by m order by null
能够看到这里没有进行排序,因为扫描是从表t的id是从1开始的,因此第一行是1
若是咱们执行下列语句,会发生什么呢?
咱们上面说的临时表,实际上是内存临时表,若是咱们把内存临时表的容量改的比咱们要查询的数据的容量小,那么就会使用到磁盘临时表,磁盘临时表的默认引擎是innodb
et tmp_table_size=1024; select id%100 as m, count(*) as c from t1 group by m order by null limit 10
group by 优化方法--直接排序
其实在上面的关于从内存临时表转化成磁盘临时表是很浪费时间的,也就是说mysql,在执行过程当中发现空间不够了,在转成磁盘临时表,可是若是咱们直接告诉mysql,我要查询的数据很大,那么mysql优化器就会想到,既然你告诉我数据很大,那么我就直接用sort_buffer进行排序,若是sort_buffer内存不够大,会用到磁盘临时表辅助排序。
select SQL_BIG_RESULT id%100 as m, count(*) as c from t1 group by m;
小结一下:
1.若是咱们不须要对统计结果进行排序,能够加上order by null省去排序流程。
2.尽可能让排序过程用上内存临时表,能够经过适当调大tmp_table_size的值来避免用到磁盘临时表。
3.若是数据量实在太大,使用SQL_BIG_RESULT告诉优化器,直接使用排序算法。