本篇內容介紹了“MySQL中的count()、union()和group by語句的用法”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
銅梁網站建設公司創(chuàng)新互聯(lián)公司,銅梁網站設計制作,有大型網站制作公司豐富經驗。已為銅梁上千家提供企業(yè)網站建設服務。企業(yè)網站搭建\成都外貿網站建設要多少錢,請找那個售后服務好的銅梁做網站的公司定做!
count()是一個聚合函數(shù),對于返回的結果集,一行行地判斷,如果count函數(shù)的參數(shù)不是NULL,累計值就加1,否則不加。最后返回累計值。【相關推薦:mysql視頻教程】
1.對于count(主鍵id)來說,InnoDB引擎會遍歷整張表,把每一行的id值都取出來,返回給server層。server層拿到id后,判斷是不可能為空的,就按行累加
2.對于count(1)來說,InnoDB引擎遍歷整張表,但不取值。server層對于返回的每一行,放一個數(shù)字1進入,判斷是不可能為空的,按行累加
3.對于count(字段)來說,如果這個字段是定義為not null的話,一行行地從記錄里面讀出這個字段,判斷不能為null,按行累加;如果這個字段定義允許為null的話,那么執(zhí)行的時候,判斷到有可能是null,還要把值取出來在判斷一下,不是null才累加
4.對于count(*)
來說,并不會把全部字段取出來,而是專門做了優(yōu)化。不取值,count(*)
肯定不是null,按行累加
為了便于量化分析,以下面表t1來舉例
create table t1(id int primary key, a int, b int, index(a)); CREATE DEFINER=`root`@`%` 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
分析下面這條SQL語句:
(select 1000 as f) union (select id from t1 order by id desc limit 2);
union的語義是取這兩個子查詢結果的并集。并集的意思是這兩個集合加起來,重復的行只保留一行
第二行的key=PRIMARY,說明第二個子句用到了索引id
第三行的Extra字段,表示在對子查詢的結果集做union的時候,使用了臨時表
這個語句的執(zhí)行流程如下:
1.創(chuàng)建一個內存臨時表,這個臨時表只有一個整型字段f,并且f是主鍵字段
2.執(zhí)行第一個子查詢,得到1000這個值
3.執(zhí)行第二個子查詢:
拿到第一行id=1000,試圖插入臨時表中。但由于1000這個值已經存在于臨時表了,違反了唯一性約束,所以插入失敗,然后繼續(xù)執(zhí)行
取到第二行id=999,插入臨時表成功
4.從臨時表中按行取出數(shù)據(jù),返回結果,并刪除臨時表,結果中包含兩行數(shù)據(jù)分別是1000和999
這里的內存臨時表起到了暫存數(shù)據(jù)的作用,而且計算過程還用上了臨時表主鍵id的唯一性約束,實現(xiàn)了union的語義
如果把上面的語句中union改成union all的話,就沒有了去重的語義。這樣執(zhí)行的時候,就依次執(zhí)行子查詢,得到的結果直接作為結果集的一部分,發(fā)給客戶端。因此也就不需要臨時表了
第二行Extra字段顯示的是Using index,表示只使用了覆蓋索引,沒有用臨時表
還是使用上面的表t1,分析下面這條SQL語句:
select id%10 as m, count(*) as c from t1 group by m;
這個語句的邏輯是把表t1里的數(shù)據(jù),按照id%10進行分組統(tǒng)計,并按照m的結果排序后輸出。explain結果如下:
在Extra字段里面,可以看到三個信息:
Using index,表示這個語句使用了覆蓋索引,選擇了索引a,不需要回表
Using temporary,表示使用了臨時表
Using filesort,表示需要排序
這個語句的執(zhí)行流程如下:
1.創(chuàng)建內存臨時表,表里有兩個字段m和c,主鍵是m
2.掃描表t1的索引a,依次取出葉子節(jié)點上的id值,計算id%10的結果,記為x
如果臨時表中沒有主鍵為x的行,就插入一個記錄(x,1)
如果表中有主鍵為x的行,就將x這一行的c值加1
3.遍歷完成后,再根據(jù)字段m做排序,得到結果集返回給客戶端
內存臨時表排序流程圖:
如果并不需要對結果進行排序,在SQL語句末尾增加order by null:
select id%10 as m, count(*) as c from t1 group by m order by null;
由于表t1中的id值是從1開始的,因此返回的結果集中第一行是id=1
這個例子里由于臨時表只有10行,內存可以放得下,因此全程只使用了內存臨時表。但是,內存臨時表的大小是有限的,參數(shù)tmp_table_size就是控制整個內存大小的,默認是16M
set tmp_table_size=1024; select id%100 as m, count(*) as c from t1 group by m order by null limit 10;
把內存臨時表的大小限制為最大1024字節(jié),并把語句改成id%100,這樣返回結果里有100行數(shù)據(jù)。但是,這時的內存臨時表大小不夠存下這100行數(shù)據(jù),也就是說,執(zhí)行過程中會發(fā)現(xiàn)內存臨時表大小達到了上限。那么,這時候會把內存臨時表轉成磁盤臨時表,磁盤臨時表默認使用的引擎是InnoDB
group by的語義邏輯,是統(tǒng)計不同的值的個數(shù)。但是,由于每一行的id%100的結果是無序的,所以就需要有一個臨時表來記錄并統(tǒng)計結果。那么,如果掃描過程中可以保證出現(xiàn)的數(shù)據(jù)是有序的就可以了
假設,現(xiàn)在有一個類似下圖的這么一個數(shù)據(jù)結構
如果可以確保輸入的數(shù)據(jù)是有序的,那么計算group by的時候,就只需要從左到右,順序掃描,依次累加。也就是下面這個流程:
當碰到第一個1的時候,已經知道累積了X個0,結果集里的第一行就是(0,X)
當碰到第一個2的時候,已經知道累積了Y個1,結果集里的第一行就是(1,Y)
按照這個邏輯執(zhí)行的話,掃描到整個輸入的數(shù)據(jù)結束,就可以拿到group by的結果,不需要臨時表,也需要再額外排序
在MySQL5.7版本支持了generated column機制,用來實現(xiàn)列數(shù)據(jù)的關聯(lián)更新。創(chuàng)建一個列z,在z列上創(chuàng)建一個索引
alter table t1 add column z int generated always as(id % 100), add index(z);
這樣,索引z上的數(shù)據(jù)就是有序的了。group by語句就可以改成:
select z, count(*) as c from t1 group by z;
從這個Extra字段可以看到,這個語句的執(zhí)行不再需要臨時表,也不需要排序了
在group by語句中加入SQL_BIG_RESULT這個提示,就可以告訴優(yōu)化器:這個語句涉及的數(shù)據(jù)量很大,直接用磁盤臨時表。因為磁盤臨時表是B+樹存儲,存儲效率不如數(shù)組來得高。所以MySQL優(yōu)化器直接用數(shù)組來存
select SQL_BIG_RESULT id%100 as m, count(*) as c from t1 group by m;
1.初始化sort_buffer,確定放入一個整型字段,記為m
2.掃描表t1的索引a,依次取出里面的id值,將id%100的值存入sort_buffer中
3.掃描完成后,對sort_buffer的字段m做排序(如果sort_buffer內存不夠用,就會利用磁盤臨時文件輔助排序)
4.排序完成后,就得到了一個有序數(shù)組
根據(jù)有序數(shù)組,得到數(shù)組里面的不同值,以及每個值的出現(xiàn)次數(shù)
這個語句的執(zhí)行沒有再使用臨時表,而是直接用了排序算法
“MySQL中的count()、union()和group by語句的用法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注創(chuàng)新互聯(lián)網站,小編將為大家輸出更多高質量的實用文章!