1.union執行過程
首先我們創建一個表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();
然后我們執行一下這條語句
explain select 1000 as f union (select id from t1 order by id desc limit 2)
首先說下union的語義,union的語義是取兩個結果的並集,重復的保留一行,然后我們來看下explain的結果,第二行的key=PRIMARY,說明用到了主鍵索引。
第三行的Extra的Using temporary說明用到了臨時表
下面我們看下這條語句的執行流程:
1.創建一個臨時表,只有f一個字段,且為主鍵
2.將1000這個數據插入臨時表
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告訴優化器,直接使用排序算法。