distinct 實際上和 group by 操作的實現非常相似,只不過是在 group by 之後的每組中只取出一條記錄而已。所以,distinct 的實現和 group by 的實現也基本差不多,沒有太大的區別。同樣可以通過鬆散索引掃瞄或者是緊湊索引掃瞄來實現,當然,在無法僅僅使用索引即能完成 distinct 的時候,mysql 只能通過臨時表來完成。但是,和 group by 有一點差別的是,distinct 並不需要進行排序。也就是說,在僅僅只是 distinct 操作的 query 如果無法僅僅利用索引完成操作的時候,mysql 會利用臨時表來做一次資料的「快取」,但是不會對臨時表中的資料進行 filesort 操作。當然,如果我們在進行 distinct 的時候還使用了 group by 並進行了分組,並使用了類似於 max 之類的聚合函式操作,就無法避免 filesort 了。
下面我們就通過幾個簡單的 query 示例來展示一下 distinct 的實現。
1.首先看看通過鬆散索引掃瞄完成 distinct 的操作:
sky@
localhost
: example11:
03:41>
explain
select
distinct
group_id
->
from
group_messageg
***************************
1.row
***************************
id:
1select_type
: ******
table
: group_message
type
: range
possible_keys
: null
key:
idx_gid_uid_gc
key_len: 4
ref:
null
rows
: 10
extra
: using
index
forgroup-by
1row
inset
(0.00
sec)
我們可以很清晰的看到,執行計畫中的 extra 資訊為「using index for group-by」,這代表什麼意思?為什麼我沒有進行 group by 操作的時候,執行計畫中會告訴我這裡通過索引進行了 group by 呢?其實這就是於 distinct 的實現原理相關的,在實現 distinct的過程中,同樣也是需要分組的,然後再從每組資料中取出一條返回給客戶端。而這裡的 extra 資訊就告訴我們,mysql 利用鬆散索引掃瞄就完成了整個操作。當然,如果 mysql query optimizer 要是能夠做的再人性化一點將這裡的資訊換成「using index for distinct」那就更好更容易讓人理解了,呵呵。
2.我們再來看看通過緊湊索引掃瞄的示例:
sky@
localhost
: example11:
03:53>
explain
select
distinct
user_id
->
from
group_message
->
where
group_id= 2
g***************************
1.row
***************************
id:
1select_type
: ******
table
: group_message
type
: ref
possible_keys
: idx_gid_uid_gc
key:
idx_gid_uid_gc
key_len: 4
ref:
const
rows: 4
extra
: using
where
; using
index
1row
inset
(0.00
sec)
這裡的顯示和通過緊湊索引掃瞄實現 group by 也完全一樣。實際上,這個 query 的實現過程中,mysql 會讓儲存引擎掃瞄 group_id = 2 的所有索引鍵,得出所有的 user_id,然後利用索引的已排序特性,每更換乙個 user_id 的索引鍵值的時候保留一條資訊,即可在掃瞄完所有 gruop_id = 2 的索引鍵的時候完成整個 distinct 操作。
3.下面我們在看看無法單獨使用索引即可完成 distinct 的時候會是怎樣:
sky@
localhost
: example11:
04:40>
explain
select
distinct
user_id
->
from
group_message
->
where
group_id
>
1and
group_id
< 10g
***************************
1.row
***************************
id:
1select_type
: ******
table
: group_message
type
: range
possible_keys
: idx_gid_uid_gc
key:
idx_gid_uid_gc
key_len: 4
ref:
null
rows
: 32
extra
: using
where
; using
index
; using
temporary
1row
inset
(0.00
sec)
當 mysql 無法僅僅依賴索引即可完成 distinct 操作的時候,就不得不使用臨時表來進行相應的操作了。但是我們可以看到,在 mysql 利用臨時表來完成 distinct 的時候,和處理 group by 有一點區別,就是少了 filesort。實際上,在 mysql 的分組演算法中,並不一定非要排序才能完成分組操作的,這一點在上面的 group by 優化小技巧中我已經提到過了。實際上這裡 mysql 正是在沒有排序的情況下實現分組最後完成 distinct 操作的,所以少了 filesort 這個排序操作。
4.最後再和 group by 結合試試看:
sky@
localhost
: example11:
05:06>
explain
select
distinct
max(
user_id
)->
from
group_message
->
where
group_id
>
1and
group_id
<
10->
group
bygroup_idg
***************************
1.row
***************************
id:
1select_type
: ******
table
: group_message
type
: range
possible_keys
: idx_gid_uid_gc
key:
idx_gid_uid_gc
key_len: 4
ref:
null
rows
: 32
extra
: using
where
; using
index
; using
temporary
; using
filesort
1row
inset
(0.00
sec)
最後我們再看一下這個和 group by 一起使用帶有聚合函式的示例,和上面第三個示例相比,可以看到已經多了 filesort 排序操作了,正是因為我們使用了 max 函式的緣故。要取得分組後的 max 值,又無法使用索引完成操作,只能通過排序才行了。
MySQL中EXPLAIN命令詳解
explain顯示了mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的查詢語句。使用方法,在select語句前加上explain就可以了 如 explain select surname first name form a b where a id b id...
MySQL中EXPLAIN解釋命令
explain顯示了mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的查詢語句。使用方法,在select語句前加上explain就可以了 如 explain select surname,first name form a,b where a.id b.id...
MySQL中EXPLAIN解釋命令
explain顯示了mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的查詢語句。使用方法,在select語句前加上explain就可以了 如 explain select surname,first name form a,b where a.id b.id...