京東一面:MySQL 中的 distinct 和 group by 哪個效率更高?問倒一大遍。。

来源:https://www.cnblogs.com/javastack/archive/2023/02/07/17099325.html
-Advertisement-
Play Games

先說大致的結論(完整結論在文末): 在語義相同,有索引的情況下:group by和distinct都能使用索引,效率相同。 在語義相同,無索引的情況下:distinct效率高於group by。原因是distinct 和 group by都會進行分組操作,但group by可能會進行排序,觸發fil ...


先說大致的結論(完整結論在文末):

  • 在語義相同,有索引的情況下:group by和distinct都能使用索引,效率相同。
  • 在語義相同,無索引的情況下:distinct效率高於group by。原因是distinct 和 group by都會進行分組操作,但group by可能會進行排序,觸發filesort,導致sql執行效率低下。

基於這個結論,你可能會問:

  • 為什麼在語義相同,有索引的情況下,group by和distinct效率相同?
  • 在什麼情況下,group by會進行排序操作?

帶著這兩個問題找答案。接下來,我們先來看一下distinct和group by的基礎使用。

推薦一個開源免費的 Spring Boot 最全教程:

https://github.com/javastacks/spring-boot-best-practice

distinct的使用

distinct用法
SELECT DISTINCT columns FROM table_name WHERE where_conditions;

例如:

mysql> select distinct age from student;
+------+
| age  |
+------+
|   10 |
|   12 |
|   11 |
| NULL |
+------+
4 rows in set (0.01 sec)

DISTINCT 關鍵詞用於返回唯一不同的值。放在查詢語句中的第一個欄位前使用,且作用於主句所有列。

如果列具有NULL值,並且對該列使用DISTINCT子句,MySQL將保留一個NULL值,並刪除其它的NULL值,因為DISTINCT子句將所有NULL值視為相同的值。

distinct多列去重

distinct多列的去重,則是根據指定的去重的列信息來進行,即只有所有指定的列信息都相同,才會被認為是重覆的信息。

SELECT DISTINCT column1,column2 FROM table_name WHERE where_conditions;
mysql> select distinct sex,age from student;
+--------+------+
| sex    | age  |
+--------+------+
| male   |   10 |
| female |   12 |
| male   |   11 |
| male   | NULL |
| female |   11 |
+--------+------+
5 rows in set (0.02 sec)

group by的使用

對於基礎去重來說,group by的使用和distinct類似:

單列去重

語法:

SELECT columns FROM table_name WHERE where_conditions GROUP BY columns;

執行:

mysql> select age from student group by age;
+------+
| age  |
+------+
|   10 |
|   12 |
|   11 |
| NULL |
+------+
4 rows in set (0.02 sec)
多列去重

語法:

SELECT columns FROM table_name WHERE where_conditions GROUP BY columns;

執行:

mysql> select sex,age from student group by sex,age;
+--------+------+
| sex    | age  |
+--------+------+
| male   |   10 |
| female |   12 |
| male   |   11 |
| male   | NULL |
| female |   11 |
+--------+------+
5 rows in set (0.03 sec)
區別示例

兩者的語法區別在於,group by可以進行單列去重,group by的原理是先對結果進行分組排序,然後返回每組中的第一條數據。且是根據group by的後接欄位進行去重的。

例如:

mysql> select sex,age from student group by sex;
+--------+-----+
| sex    | age |
+--------+-----+
| male   |  10 |
| female |  12 |
+--------+-----+
2 rows in set (0.03 sec)

distinct和group by原理

在大多數例子中,DISTINCT可以被看作是特殊的GROUP BY,它們的實現都基於分組操作,且都可以通過鬆散索引掃描、緊湊索引掃描(關於索引掃描的內容會在其他文章中詳細介紹,就不在此細緻介紹了)來實現。

DISTINCTGROUP BY都是可以使用索引進行掃描搜索的。例如以下兩條sql(只單單看表格最後extra的內容),我們對這兩條sql進行分析,可以看到,在extra中,這兩條sql都使用了緊湊索引掃描Using index for group-by

所以,在一般情況下,對於相同語義的DISTINCTGROUP BY語句,我們可以對其使用相同的索引優化手段來進行優化。

mysql> explain select int1_index from test_distinct_groupby group by int1_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 | Using index for group-by |
+----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set (0.05 sec)

mysql> explain select distinct int1_index from test_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 | Using index for group-by |
+----+-------------+-----------------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set (0.05 sec)

但對於GROUP BY來說,在MYSQL8.0之前,GROUP Y預設會依據欄位進行隱式排序。

可以看到,下麵這條sql語句在使用了臨時表的同時,還進行了filesort。

mysql> explain select int6_bigger_random from test_distinct_groupby GROUP BY int6_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 | Using temporary; Using filesort |
+----+-------------+-----------------------+------------+------+---------------+------+---------+------+-------+----------+---------------------------------+
1 row in set (0.04 sec)
隱式排序

對於隱式排序,我們可以參考Mysql官方的解釋:

GROUP BY implicitly sorts by default (that is, in the absence of ASC or DESC designators for GROUP BY columns). However, relying on implicit GROUP BY sorting (that is, sorting in the absence of ASC or DESC designators) or explicit sorting for GROUP BY (that is, by using explicit ASC or DESC designators for GROUP BY columns) is deprecated. To produce a given sort order, provide an ORDER BY clause.

大致解釋一下:

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時,對此進行了優化更新:

Previously (MySQL 5.7 and lower), GROUP BY sorted implicitly under certain conditions. In MySQL 8.0, that no longer occurs, so specifying ORDER BY NULL at the end to suppress implicit sorting (as was done previously) is no longer necessary. However, query results may differ from previous MySQL versions. To produce a given sort order, provide an ORDER BY clause.

大致解釋一下:

從前(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的原因

  1. group by語義更為清晰
  2. group by可對數據進行更為複雜的一些處理

相比於distinct來說,group by的語義明確。且由於distinct關鍵字會對所有欄位生效,在進行複合業務處理時,group by的使用靈活性更高,group by能根據分組情況,對數據進行更為複雜的處理,例如通過having對數據進行過濾,或通過聚合函數對數據進行運算。

版權聲明:本文為CSDN博主「猾梟」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。原文鏈接:https://blog.csdn.net/weixin_42615847/article/details/118342524

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 視圖和視圖解析器 1.基本介紹 在SpringMVC中的目標方法,最終返回的都是一個視圖(有各種視圖) 註意,這裡的視圖是一個類對象,不是一個頁面!! 返回的視圖都會由一個視圖解析器來處理(視圖解析器有很多種) 2.自定義視圖 2.1為什麼需要自定義視圖 在預設情況下,我們都是返回預設的視圖,然後返 ...
  • 這篇文章主要描述分散式技術中的選舉演算法,分散式選舉是為選出一個主節點,由它來協調和管理其他節點,保證集群有序運行和節點間數據的一致性。涉及到的選舉演算法包括Bully演算法、Raft演算法和ZAB演算法。 ...
  • 最近在寫一本Xilinx的FPGA方面的書,現將HLS部分內容在這裡分享給大家,希望大家喜歡,也歡迎批評指正。[原創www.cnblogs.com/helesheng] 在可編程邏輯器件被用於電子系統設計的前期,由於所含的邏輯資源較少,絕大部分情況下,它們被用於實現數據的傳輸和介面電路。工程師們習慣 ...
  • 1 簡介 AOP,即面向切麵編程是很常用的技術,特別是在Java Web開發中。而最流行的AOP框架分別是Spring AOP和AspectJ。 2 Spring AOP vs AspectJ Spring AOP是基於Spring IoC實現的,它解決大部分常見的需求,但它並不是一個完整的AOP解 ...
  • 原文地址:https://bysocket.com/openai-chatgpt-error-1020/ 最近打開 chat.openai.com/chat 地址,如下圖會提示錯誤碼:Access denied Error code 1020。這裡通過學習和排查,總結了 3 種方法區解決這個問題。 ...
  • 這次設計一個VGA、TFT顯示模塊,其特點如下: 行同步信號、場同步信號、數據有效信號的延遲數可調。(應用時方便與存儲模塊數據對齊) 解析度可以通過調整參數來改變。 數據格式為RGR565,可簡單修改位寬來修改成其他數據格式。 TFT的介面時序和VGA的時序相似,但是TFT介面比VGA多了數據有效信 ...
  • spi是原生java的組件,通過META-INF/services目錄進行註冊,通過ServiceLoader進行載入,一般可以用在組件開發中,你在公用組件中封裝好邏輯,將個性化的部分抽象出一個介面,介面通過spi的方式進行載入,在外部開發人員引用你的組件之後,通過實現介面來擴展個性化的功能,再通過 ...
  • 使用 SpringBoot 提供 api 的時候,我更喜歡使用 jwt 的方式來做驗證。網上有會多 Spring Security 整合 jwt 的,也有 Shiro 整合 jwt 的,感覺有點複雜。這裡分享一下自己在項目中的簡單實現。 依賴包 除了 SpringBoot 基本的依賴,需要一個生成 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...