union执行流程
创建一个表t2,主键id,字段a添加索引,字段b无索引,并且按照递增插入1000条数据,id和a,b都是从1到1000。
然后执行一条sql:
explain select 1000 as f union (select id from t2 order by id desc limit 2)
这里使用到了union,所以在两个子查询的结果还需要做去重处理,可以看到解析结果是使用了临时表的。
执行流程大概如下:
- 创建一个临时表,字段只有f ,并且f是主键字段(为啥是主键字段?因为只有一个?还是因为第一个出现字段?还是因为需要根据这个去重?)
- 执行第一个子查询,将1000放入临时表中
- 执行第二个子查询,先查询到id=1000,准备放入临时表中,但临时表中已经有了一个1000,违反唯一性约束,放入失败;继续查询id=999可以放入临时表中
- 从临时表中取出所有数据,返回结果,也就是1000和999,删除临时表。
把上面的查询语句修改一下:
explain select 1000 as f union on (select id from t2 order by id desc limit 2)
把union 改成了 union all,也就是不需要去重了,没有了对数据的进一步处理:
可以看到这里就没有使用到临时表,因为没有对查询结果的进一步处理,直接就可以返回结果。
group by 执行流程
执行一条sql语句:
explain
select
id%10 as m,
count(1) as c
from
t2
group by
m
执行结果分析中看到是使用了覆盖索引和临时表的。执行流程大概如下:
- 创建临时表,有两个字段,一个m,一个c(也就是select字段中取得别名)
- 扫描表索引字段a,不需要回表就可以直接取得主键id字段的值,计算i%10,得到结果x,将结果放入临时表,如果临时表中没有m列的值等于x的,就插入一条记录(x,1);如果x已经存在了,就将c列的值加上一。
修改一下sql语句:
explain
select
id%10 as m,
count(1) as c
from
t2
group by
id
执行结果中就不会使用临时表,区别就是使用了索引字段,如果将上面sql的id字段都换成b字段,会发现执行结果分析就会有临时表。
临时表的大小是通过参数tmp_table_size来控制的。
如果group by字段中没有索引,而且放入到临时表的数据量很大,超过了临时表大小的限制,就会转为磁盘临时表。
如果事先知道数据量很大,可以直接指定使用磁盘临时表;
修改一下sql:
explain
select
sql_BIG_RESULT b%10 as m,
count(1) as c
from
t2
group by
b
可以看到执行结果分析中只使用了排序,没有使用临时表。group by 有索引的字段和group by 无索引的字段的区别就是数据是否是排序过的,如果排序过的,对于上面sql出现的count(1)函数就可以直接统计出现的次数,而不需要去出现一次计数一个。
MySQL什么时候会使用内部临时表?
- 如果语句执行过程可以一边读数据,一边直接得到结果,是不需要额外内存的,否则就需要额外的内存,来保存中间结果;
- 如果执行逻辑需要用到二维表特性,就会优先考虑使用临时表。比如union 需要用到唯一索引约束, group by还需要用到另外一个字段来存累积计数。