distinct与groupby的用法groupby

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

由於GROUP BY 實際上也同樣會進行排序操作,而且與ORDER BY 相比,GROUP BY 主要只是多了排序之後的分組操作。

當然,如果在分組的時候還使用了其他的一些聚合函數,那麼還需要一些聚合函數的計算。

所以,在GROUP BY 的實現過程中,與ORDER BY 一樣也可以利用到索引。

在MySQL 中,GROUP BY 的實現同樣有多種(三種)方式,其中有兩種方式會利用現有的索引信息來完成GROUP BY,另外一種爲完全無法使用索引的場景下使用。

下面我們分別針對這三種實現方式做一個分析。

1.使用鬆散(Loose)索引掃描實現GROUP BY
何謂鬆散索引掃描實現GROUP BY 呢?實際上就是當MySQL 完全利用索引掃描來實現GROUP BY 的時候,並不需要掃描所有滿足條件的索引鍵即可完成操作得出結果。

下面我們通過一個示例來描述鬆散索引掃描實現GROUP BY,在示例之前我們需要首先調整一下group_message 表的索引,將gmt_create 字段添加到group_id 和user_id 字段的索引中:
sky@localhost : example08:49:45> createindexidx_gid_uid_gc
-> ongroup_message(group_id,user_id,gmt_create);
QueryOK, rowsaffected(0.03sec)
Records: 96 Duplicates: 0 Warnings: 0
sky@localhost : example09:07:30> dropindexidx_group_message_gid_uid
-> ongroup_message;
QueryOK, 96rowsaffected(0.02sec)
Records: 96 Duplicates: 0 Warnings: 0
然後再看如下Query 的執行計劃:
sky@localhost : example09:26:15> EXPLAIN
-> SELECTuser_id,max(gmt_create)
-> FROMgroup_message
-> WHEREgroup_id < 10
-> GROUPBYgroup_id,user_id\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: group_message
type: range
possible_keys: idx_gid_uid_gc
key: idx_gid_uid_gc
key_len: 8
ref: NULL
rows: 4
Extra: Usingwhere; Usingindexforgroup-by
1rowinset(0.00sec)
我們看到在執行計劃的Extra 信息中有信息顯示“Using index for group-by”,實際上這就是告訴我們,MySQL Query Optimizer 通過使用鬆散索引掃描來實現了我們所需要的GROUP BY 操作。

下面這張圖片描繪了掃描過程的大概實現:
要利用到鬆散索引掃描實現GROUP BY,需要至少滿足以下幾個條件:
◆GROUP BY 條件字段必須在同一個索引中最前面的連續位置;
◆在使用GROUP BY 的同時,只能使用MAX 和MIN 這兩個聚合函數;
◆如果引用到了該索引中GROUP BY 條件之外的字段條件的時候,必須以常量形式存在;
爲什麼鬆散索引掃描的效率會很高?
因爲在沒有WHERE子句,也就是必須經過全索引掃描的時候,鬆散索引掃描需要讀取的鍵值數量與分組的組數量一樣多,也就是說比實際存在的鍵值數目要少很多。

而在WHERE 子句包含範圍判斷式或者等值表達式的時候,鬆散索引掃描查找滿足範圍條件的每個組的第1個關鍵字,並且再次讀取儘可能最少數量的關鍵字。

2.使用緊湊(Tight)索引掃描實現GROUP BY
緊湊索引掃描實現GROUP BY 和鬆散索引掃描的區別主要在於他需要在掃描索引的時候,讀取所有滿足條件的索引鍵,然後再根據讀取惡的數據來完成GROUP BY 操作得到相應結果。

sky@localhost : example08:55:14> EXPLAIN
-> SELECTmax(gmt_create)
-> FROMgroup_message
-> WHEREgroup_id = 2
-> GROUPBYuser_id\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: group_message
type: ref
possible_keys: idx_group_message_gid_uid,idx_gid_uid_gc
key: idx_gid_uid_gc
key_len: 4
ref: const
rows: 4
Extra: Usingwhere; Usingindex
1rowinset(0.01sec)
這時候的執行計劃的Extra 信息中已經沒有“Using index for group-by”了,但並不是說MySQL 的GROUP BY 操作並不是通過索引完成的,只不過是需要訪問WHERE 條件所限定的所有索引鍵信息之後才能得出結果。

這就是通過緊湊索引掃描來實現GROUP BY 的執行計劃輸出信息。

下面這張圖片展示了大概的整個執行過程:
在MySQL 中,MySQL Query Optimizer 首先會選擇嘗試通過鬆散索引掃描來實現GROUP BY 操作,當發現某些情況無法滿足鬆散索引掃描實現GROUP BY 的要求之後,纔會嘗試通過緊湊索引掃描來實現。

當GROUP BY 條件字段並不連續或者不是索引前綴部分的時候,MySQL Query Optimizer 無法使用鬆散索引掃描,設置無法直接通過索引完成GROUP BY 操作,因爲缺失的索引鍵信息無法得到。

但是,如果Query 語句中存在一個常量值來引用缺失的索引鍵,則可以使用緊湊索引掃描完成GROUP BY 操作,因爲常量填充了搜索關鍵字中的“差距”,可以形成完整的索引前綴。

這些索引前綴可以用於索引查找。

而如果需要排序GROUP BY結果,並且能夠形成索引前綴的搜索關鍵字,MySQL還可以避免額外的排序操作,因爲使用有順序的索引的前綴進行搜索已經按順序檢索到了所有關鍵字。

3.使用臨時表實現GROUP BY
MySQL 在進行GROUP BY 操作的時候要想利用所有,必須滿足GROUP BY 的字段必須同時存放於同一個索引中,且該索引是一個有序索引(如Hash 索引就不能滿足要求)。

而且,並不只是如此,是否能夠利用索引來實現GROUP BY 還與使用的聚合函數也有關係。

前面兩種GROUP BY 的實現方式都是在有可以利用的索引的時候使用的,當MySQL Query Optimizer 無法找到合適的索引可以利用的時候,就不得不先讀取需要的數據,然後通過臨時表來完成GROUP BY 操作。

sky@localhost : example09:02:40> EXPLAIN
-> SELECTmax(gmt_create)
-> FROMgroup_message
-> WHEREgroup_id > 1andgroup_id < 10
-> GROUPBYuser_id\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: group_message
type: range
possible_keys: idx_group_message_gid_uid,idx_gid_uid_gc
key: idx_gid_uid_gc
key_len: 4
ref: NULL
rows: 32
Extra: Usingwhere; Usingindex; Usingtemporary; Usingfilesort
這次的執行計劃非常明顯的告訴我們MySQL 通過索引找到了我們需要的數據,然後創建了臨時表,又進行了排序操作,纔得到我們需要的GROUP BY 結果。

整個執行過程大概如下圖所展示:
當MySQL Query Optimizer 發現僅僅通過索引掃描並不能直接得到GROUP BY 的結果之後,他就不得不選擇通過使用臨時表然後再排序的方式來實現GROUP BY了。

在這樣示例中即是這樣的情況。

group_id 並不是一個常量條件,而是一個範圍,而且GROUP BY 字段爲user_id。

所以MySQL 無法根據索引的順序來幫助GROUP BY 的實現,只能先通過索引範圍掃描得到需要的數據,然後將數據存入臨時表,然後再進行排序和分組操作來完成GROUP BY。

相关文档
最新文档