結論
先說大致的結論:
在語義相同,有索引的情況下:group by和distinct都能使用索引,效率相同。
在語義相同,無索引的情況下:distinct效率高于group by。原因是distinct 和 group by都會進行分組操作,但group by可能會進行排序,觸發filesort,導致sql執行效率低下。
基于這個結論,你可能會問:
為什么在語義相同,有索引的情況下,group by和distinct效率相同?
在什么情況下,group by會進行排序操作?
帶著這兩個問題找答案。接下來,我們先來看一下distinct和group by的基礎使用。
基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
distinct的使用
distinct用法
SELECTDISTINCTcolumnsFROMtable_nameWHEREwhere_conditions;
例如:
mysql>selectdistinctagefromstudent; +------+ |age| +------+ |10| |12| |11| |NULL| +------+ 4rowsinset(0.01sec)
DISTINCT 關鍵詞用于返回唯一不同的值。放在查詢語句中的第一個字段前使用,且作用于主句所有列。
如果列具有NULL值,并且對該列使用DISTINCT子句,MySQL將保留一個NULL值,并刪除其它的NULL值,因為DISTINCT子句將所有NULL值視為相同的值。
distinct多列去重
distinct多列的去重,則是根據指定的去重的列信息來進行,即只有所有指定的列信息都相同,才會被認為是重復的信息。
SELECTDISTINCTcolumn1,column2FROMtable_nameWHEREwhere_conditions; mysql>selectdistinctsex,agefromstudent; +--------+------+ |sex|age| +--------+------+ |male|10| |female|12| |male|11| |male|NULL| |female|11| +--------+------+ 5rowsinset(0.02sec)
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
group by的使用
對于基礎去重來說,group by的使用和distinct類似:
單列去重
語法:
SELECTcolumnsFROMtable_nameWHEREwhere_conditionsGROUPBYcolumns;
執行:
mysql>selectagefromstudentgroupbyage; +------+ |age| +------+ |10| |12| |11| |NULL| +------+ 4rowsinset(0.02sec)
多列去重
語法:
SELECTcolumnsFROMtable_nameWHEREwhere_conditionsGROUPBYcolumns;
執行:
mysql>selectsex,agefromstudentgroupbysex,age; +--------+------+ |sex|age| +--------+------+ |male|10| |female|12| |male|11| |male|NULL| |female|11| +--------+------+ 5rowsinset(0.03sec)
區別示例
兩者的語法區別在于,group by可以進行單列去重,group by的原理是先對結果進行分組排序,然后返回每組中的第一條數據。且是根據group by的后接字段進行去重的。
例如:
mysql>selectsex,agefromstudentgroupbysex; +--------+-----+ |sex|age| +--------+-----+ |male|10| |female|12| +--------+-----+ 2rowsinset(0.03sec)
distinct和group by原理
在大多數例子中,DISTINCT可以被看作是特殊的GROUP BY,它們的實現都基于分組操作,且都可以通過松散索引掃描、緊湊索引掃描(關于索引掃描的內容會在其他文章中詳細介紹,就不在此細致介紹了)來實現。
DISTINCT和GROUP BY都是可以使用索引進行掃描搜索的。例如以下兩條sql(只單單看表格最后extra的內容),我們對這兩條sql進行分析,可以看到,在extra中,這兩條sql都使用了緊湊索引掃描Using index for group-by。
所以,在一般情況下,對于相同語義的DISTINCT和GROUP BY語句,我們可以對其使用相同的索引優化手段來進行優化。
mysql>explainselectint1_indexfromtest_distinct_groupbygroupbyint1_index; +----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ |id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra| +----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ |1|SIMPLE|test_distinct_groupby|NULL|range|index_1|index_1|5|NULL|955|100.00|Usingindexforgroup-by| +----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ 1rowinset(0.05sec) mysql>explainselectdistinctint1_indexfromtest_distinct_groupby; +----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ |id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra| +----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ |1|SIMPLE|test_distinct_groupby|NULL|range|index_1|index_1|5|NULL|955|100.00|Usingindexforgroup-by| +----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ 1rowinset(0.05sec)
但對于GROUP BY來說,在MYSQL8.0之前,GROUP Y默認會依據字段進行隱式排序。
可以看到,下面這條sql語句在使用了臨時表的同時,還進行了filesort。
mysql>explainselectint6_bigger_randomfromtest_distinct_groupbyGROUPBYint6_bigger_random; +----+-------------+-----------------------+------------+------+---------------+------+---------+------+-------+----------+---------------------------------+ |id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra| +----+-------------+-----------------------+------------+------+---------------+------+---------+------+-------+----------+---------------------------------+ |1|SIMPLE|test_distinct_groupby|NULL|ALL|NULL|NULL|NULL|NULL|97402|100.00|Usingtemporary;Usingfilesort| +----+-------------+-----------------------+------------+------+---------------+------+---------+------+-------+----------+---------------------------------+ 1rowinset(0.04sec)
隱式排序
對于隱式排序,我們可以參考Mysql官方的解釋
大致解釋一下:
GROUP BY 默認隱式排序(指在 GROUP BY 列沒有 ASC 或 DESC 指示符的情況下也會進行排序)。然而,GROUP BY進行顯式或隱式排序已經過時(deprecated)了,要生成給定的排序順序,請提供 ORDER BY 子句。
所以,在Mysql8.0之前,Group by會默認根據作用字段(Group by的后接字段)對結果進行排序。在能利用索引的情況下,Group by不需要額外進行排序操作;但當無法利用索引排序時,Mysql優化器就不得不選擇通過使用臨時表然后再排序的方式來實現GROUP BY了。
且當結果集的大小超出系統設置臨時表大小時,Mysql會將臨時表數據copy到磁盤上面再進行操作,語句的執行效率會變得極低。這也是Mysql選擇將此操作(隱式排序)棄用的原因。
基于上述原因,Mysql在8.0時,對此進行了優化更新:
大致解釋一下:
從前(Mysql5.7版本之前),Group by會根據確定的條件進行隱式排序。在mysql 8.0中,已經移除了這個功能,所以不再需要通過添加order by null 來禁止隱式排序了,但是,查詢結果可能與以前的 MySQL 版本不同。要生成給定順序的結果,請按通過ORDER BY指定需要進行排序的字段。
因此,我們的結論也出來了:
在語義相同,有索引的情況下:
group by和distinct都能使用索引,效率相同。因為group by和distinct近乎等價,distinct可以被看做是特殊的group by。
在語義相同,無索引的情況下:
distinct效率高于group by。原因是distinct 和 group by都會進行分組操作,但group by在Mysql8.0之前會進行隱式排序,導致觸發filesort,sql執行效率低下。
但從Mysql8.0開始,Mysql就刪除了隱式排序,所以,此時在語義相同,無索引的情況下,group by和distinct的執行效率也是近乎等價的。
推薦group by的原因
group by語義更為清晰
group by可對數據進行更為復雜的一些處理
相比于distinct來說,group by的語義明確。且由于distinct關鍵字會對所有字段生效,在進行復合業務處理時,group by的使用靈活性更高,group by能根據分組情況,對數據進行更為復雜的處理,例如通過having對數據進行過濾,或通過聚合函數對數據進行運算。
審核編輯:劉清
-
MySQL
+關注
關注
1文章
809瀏覽量
26575 -
null
+關注
關注
0文章
19瀏覽量
3974
原文標題:面試官:MySQL中的 distinct 和 group by 哪個效率更高?
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論