mysql中group by實現方式有三種,松散索引,緊湊索引,臨時文件(文件排序)。
在網上看了相關的介紹,大部分介紹都比較晦澀難懂,這里說下我的理解。
在學習SQL優化時,我們都知道可以對group by進行優化的方式就是對group by引用的字段建立索引。當group by引用多個字段時,我們建立的相應的索引也應包含多個字段。
對group by操作優化的原理就是讓mysql利用索引,而避免進行建立臨時表,進而進行文件排序(group by的第三種實現方式)。
對於group by引用的多個字段,需滿足於所建立索引的最左前綴索引,否則進行group by操作時,無法利用索引。在利用索引時,group by可根據索引,即可對數據分組,此時完全不用去
訪問表的數據值(索引健對應的數據)。這種實現方式就是利用松散索引。
當group by引用的字段無法構成所建索引的最左前綴索引時,也就是說group by不能利用索引時。如何where語句(如果有的話)彌補了這種差距,比如:group by
引用的字段為(c2,c3),而索引為(c1,c2,c3)。此時如果where語句限定了c1=a(某一個值),那么此時mysql的執行過程為先根據where語句進行一次選擇,
對選出來的結果集,可以利用索引。這種方式,從整體上來說,group by並沒有利用索引,但是從過程來說,在選出的結果中利用了索引,這種方式就是緊湊索引。
這種方式,mysql的執行計划為using where,use index。而松散索引的執行計划為using index for group by。
如果mysql如論如何都不能利用索引時,此時mysql將讀取所有的數據建立臨時表,對文件進行排序,完成分組操作。
關於group的作用:
滿足於我們針對某些字段進行分組,然后在組內對多行數據進行處理(計算行數,計算max,min等等)這樣的需求。group by返回的數據是有序的。
如果我們對數據進行分組后,要輸出(select)其所有(多)行的數據,此時是無法實現的。根據版本的不同,可能會輸出第一行數據,可能會保錯。
轉:https://blog.csdn.net/qq403580298/article/details/90756352
寫在前面的話:用了好久group by,今天早上一覺醒來,突然感覺group by好陌生,總有個筋別不過來,為什么不能夠select * from Table group by id,為什么一定不能是*,而是某一個列或者某個列的聚合函數,group by 多個字段可以怎么去很好的理解呢?不過最后還是轉過來了,簡單寫寫吧,大牛們直接略過吧。
=========正文開始===========
先來看下表1,表名為test:
表1
執行如下SQL語句:
1
2
|
SELECT
name
FROM
test
GROUP
BY
name
|
你應該很容易知道運行的結果,沒錯,就是下表2:
表2
可是為了能夠更好的理解“group by”多個列“和”聚合函數“的應用,我建議在思考的過程中,由表1到表2的過程中,增加一個虛構的中間表:虛擬表3。下面說說如何來思考上面SQL語句執行情況:
1.FROM test:該句執行后,應該結果和表1一樣,就是原來的表。
2.FROM test Group BY name:該句執行后,我們想象生成了虛擬表3,如下所圖所示,生成過程是這樣的:group by name,那么找name那一列,具有相同name值的行,合並成一行,如對於name值為aa的,那么<1 aa 2>與<2 aa 3>兩行合並成1行,所有的id值和number值寫到一個單元格里面。
3.接下來就要針對虛擬表3執行Select語句了:
(1)如果執行select *的話,那么返回的結果應該是虛擬表3,可是id和number中有的單元格里面的內容是多個值的,而關系數據庫就是基於關系的,單元格中是不允許有多個值的,所以你看,執行select * 語句就報錯了。
(2)我們再看name列,每個單元格只有一個數據,所以我們select name的話,就沒有問題了。為什么name列每個單元格只有一個值呢,因為我們就是用name列來group by的。
(3)那么對於id和number里面的單元格有多個數據的情況怎么辦呢?答案就是用聚合函數,聚合函數就用來輸入多個數據,輸出一個數據的。如cout(id),sum(number),而每個聚合函數的輸入就是每一個多數據的單元格。
(4)例如我們執行select name,sum(number) from test group by name,那么sum就對虛擬表3的number列的每個單元格進行sum操作,例如對name為aa的那一行的number列執行sum操作,即2+3,返回5,最后執行結果如下:
(5)group by 多個字段該怎么理解呢:如group by name,number,我們可以把name和number 看成一個整體字段,以他們整體來進行分組的。如下圖
(6)接下來就可以配合select和聚合函數進行操作了。如執行select name,sum(id) from test group by name,number,結果如下圖:
至此,我已經對我自己對如此簡單的問題有如此天馬行空的想法所折服,洗洗睡覺。