【MySQL】分組排序取每組第一條數據

需求:MySQL根據某一個字段分組,然后組內排序,最后每組取排序后的第一條數據。

準備表:

CREATE TABLE `t_student_score` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',`stu_name` varchar(32) NOT NULL COMMENT '學生姓名',`course_name` varchar(32) NOT NULL COMMENT '課程名稱',`score` int(11) NOT NULL COMMENT '份數',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='學生-分數';

準備數據:

INSERT INTO `t_student_score` (`id`, `stu_name`, `course_name`, `score`) VALUES (1, '張三', '數學', 90);
INSERT INTO `t_student_score` (`id`, `stu_name`, `course_name`, `score`) VALUES (2, '李四', '語文', 94);
INSERT INTO `t_student_score` (`id`, `stu_name`, `course_name`, `score`) VALUES (3, '張三', '語文', 98);
INSERT INTO `t_student_score` (`id`, `stu_name`, `course_name`, `score`) VALUES (4, '李四', '數學', 97);
INSERT INTO `t_student_score` (`id`, `stu_name`, `course_name`, `score`) VALUES (5, '李四', '英語', 99);
INSERT INTO `t_student_score` (`id`, `stu_name`, `course_name`, `score`) VALUES (6, '張三', '英語', 100);

數據如下:

mysql> select * from t_student_score;
+----+----------+-------------+-------+
| id | stu_name | course_name | score |
+----+----------+-------------+-------+
|  1 | 張三     | 數學        |    90 |
|  2 | 李四     | 語文        |    94 |
|  3 | 張三     | 語文        |    98 |
|  4 | 李四     | 數學        |    97 |
|  5 | 李四     | 英語        |    99 |
|  6 | 張三     | 英語        |   100 |
+----+----------+-------------+-------+
6 rows in set (0.08 sec)

要求:查詢出各科分數最高的學生姓名。

group by

查詢出各科分數最高的學生姓名一開始可能會這樣寫:

select stu_name,course_name,max(score) from t_student_score group by course_name;

sql中只是簡單的按課程進行分組,這樣寫就會導致一個問題也就是查詢出來的各科最高分數可能不是那個學生的,結果如下:

mysql> select stu_name,course_name,max(score) from t_student_score group by course_name;
+----------+-------------+------------+
| stu_name | course_name | max(score) |
+----------+-------------+------------+
| 張三     | 數學        |         97 |
| 李四     | 英語        |        100 |
| 李四     | 語文        |         98 |
+----------+-------------+------------+
3 rows in set (0.05 sec)

很明顯數學得97分的壓根就不是張三,這是為什么呢,group by后的顯示的列會只會根據所有組的第一行來顯示,張三剛好在數學組的第一行,所以出來的是張三。

group by+子查詢order by

既然我們知道group by后的顯示的列會只會根據所有組的第一行來顯示,那么我們先根據分數進行排序,這樣分數最高的肯定是所有組的第一行,然后根據課程進行分組這樣是不是就對了?

mysql> select stu_name,course_name,max(score) from (select * from t_student_score order by score desc) t group by course_name;
+----------+-------------+------------+
| stu_name | course_name | max(score) |
+----------+-------------+------------+
| 張三     | 數學        |         97 |
| 李四     | 英語        |        100 |
| 李四     | 語文        |         98 |
+----------+-------------+------------+
3 rows in set (0.13 sec)

什么情況,以前我怎么記得這么使用是對的呢?然后去查看SQL的執行計劃:

mysql> explain select stu_name,course_name,max(score) from (select * from t_student_score order by score desc) t group by course_name;
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
| id | select_type | table           | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                           |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
|  1 | SIMPLE      | t_student_score | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    6 |   100.00 | Using temporary; Using filesort |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
1 row in set (0.06 sec)

執行計劃顯示只有一個步驟,為什么不是分為兩個步驟執行呢?第一步先根據表t_student_score的score字段進行倒序排序,第二步根據第一步生成的臨時表t的course_name字段進行分組???

而在MySQL5.6中,執行上面的sql會出現不一樣的結果:

mysql> select stu_name,course_name,max(score) from (select * from t_student_score order by score desc) t group by course_name;
+----------+-------------+------------+
| stu_name | course_name | max(score) |
+----------+-------------+------------+
| 李四     | 數學        |         97 |
| 張三     | 英語        |        100 |
| 張三     | 語文        |         98 |
+----------+-------------+------------+
3 rows in set (0.10 sec)

MySQL5.6中返回的結果正是我們想要的。

再來看下MySQL5.6中這個SQL的執行計劃:

mysql> explain select stu_name,course_name,max(score) from (select * from t_student_score order by score desc) t group by course_name;
+----+-------------+-----------------+------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table           | type | possible_keys | key  | key_len | ref  | rows | Extra                           |
+----+-------------+-----------------+------+---------------+------+---------+------+------+---------------------------------+
|  1 | PRIMARY     | <derived2>      | ALL  | NULL          | NULL | NULL    | NULL |    6 | Using temporary; Using filesort |
|  2 | DERIVED     | t_student_score | ALL  | NULL          | NULL | NULL    | NULL |    6 | Using filesort                  |
+----+-------------+-----------------+------+---------------+------+---------+------+------+---------------------------------+
2 rows in set (0.09 sec)

MySQL5.6中這個SQL的執行計劃分為兩個步驟執行的。

那么為什么切換了版本后就好了呢?

derived_merge

MySQL5.7針對于5.6版本做了一個優化,針對MySQL本身的優化器增加了一個控制優化器的參數叫derived_merge,什么意思呢,“派生類合并”。

官方文檔介紹:https://dev.mysql.com/doc/refman/5.7/en/derived-table-optimization.html

使用合并或實現來優化派生表和視圖引用優化器可以使用兩種策略(也適用于視圖引用)處理派生表引用:

  • 將派生表合并到外部查詢塊中
  • 將派生表實現為內部臨時表

例如:

SELECT * FROM (SELECT *FROM t1) AS derived_t1

通過合并派生表derived_t1,該查詢的執行類似于:

SELECT * FROM t1;

原來是派生類合并在作怪,通過對MySQL官方使用手冊的了解,MySQL5.7對derived_merge參數默認設置為on,也就是開啟狀態,我們在MySQL5.7中把這個特性關閉使用就行了,如下命令:

# 針對當前session關閉
set session optimizer_switch="derived_merge=off";# 全局關閉
set global optimizer_switch="derived_merge=off";

這樣如果from中查詢出來的的結果就不會與外部查詢塊合并了,sql執行結果如下:

mysql> set session optimizer_switch="derived_merge=off";
Query OK, 0 rows affected (0.01 sec)mysql> select stu_name,course_name,max(score) from (select * from t_student_score order by score desc) t group by course_name;
+----------+-------------+------------+
| stu_name | course_name | max(score) |
+----------+-------------+------------+
| 李四     | 數學        |         97 |
| 張三     | 英語        |        100 |
| 張三     | 語文        |         98 |
+----------+-------------+------------+
3 rows in set (0.07 sec)mysql> explain select stu_name,course_name,max(score) from (select * from t_student_score order by score desc) t group by course_name;
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
| id | select_type | table           | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                           |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
|  1 | PRIMARY     | <derived2>      | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    6 |   100.00 | Using temporary; Using filesort |
|  2 | DERIVED     | t_student_score | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    6 |   100.00 | Using filesort                  |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
2 rows in set (0.10 sec)

其實修改derived_merge參數得謹慎而行之,因為MySQL5.7版本有了這個優化的機制是有它的道理的,之所以去除派生類與外部塊合并,是因為減少查詢開銷,派生類是個臨時表,開辟一個臨時表的同時還要維護和排序或者分組,都會影響效率,所以盡量不要去修改此參數。

其實也有多種辦法不需要修改derived_merge參數而使合并派生類失效,具體做法可參考官方使用手冊,可以通過在子查詢中使用任何阻止合并的構造來禁用合并,盡管這些構造對實現的影響并不明確。

防止合并的構造對于派生表和視圖引用是相同的:

  • 聚合函數(SUM(),MIN(),MAX(),COUNT()等)
  • DISTINCT
  • GROUP BY
  • HAVING
  • LIMIT
  • UNION或UNION ALL
  • 選擇列表中的子查詢
  • 分配給用戶變量
  • 僅引用文字值(在這種情況下,沒有基礎表)

下面通過在子查詢中使用distinct關鍵字來禁用derived_merge:

mysql> explain select stu_name,course_name,max(score) from (select distinct(id) tid,s.* from t_student_score s order by score desc) t group by course_name;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                           |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    6 |   100.00 | Using temporary; Using filesort |
|  2 | DERIVED     | s          | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    6 |   100.00 | Using filesort                  |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
2 rows in set (0.08 sec)

子查詢order by失效的場景

因為臨時表(派生表derived table)中使用order by且使其生效,必須滿足三個條件:

  • 外部查詢禁止分組或者聚合
  • 外部查詢未指定having, order by
  • 外部查詢將派生表或者視圖作為from句中唯一指定源

不滿足這三個條件,order by會被忽略。

一旦外部表使用了group by,那么臨時表(派生表 derived table)將不會執行filesort操作(即order by 會被忽略)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11799.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11799.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11799.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

NumPy常用操作

目錄 一&#xff1a;簡介 二&#xff1a;NumPy 常用操作 三&#xff1a;總結 一&#xff1a;簡介 是一個開源的Python庫&#xff0c;它為Python提供了強大的多維數組對象和用于處理這些數組的函數。NumPy的核心是ndarray&#xff0c;它是一個高效的多維數組容器&#xff0c;用…

力扣【旋轉函數】python

如果直接用暴力的話&#xff0c;只能過4個樣例好像&#xff0c;超時 因此得用遞推公式 F1F0前n-1個數-(n-1)*第n個數 F0sum(nums)-n*第n個數 nlen(nums) ans[]#定義一個存最大值值的列表 ss sum(nums) dm 0 for j in range(n):dm j * nums[j] ans.append(dm) print(dm) n…

springmvc返回json

springmvc返回json 現在很多項目已經前后端分離了&#xff0c;不再使用jsp或者使用jsp但是數據使用ajax來獲取&#xff0c;實現局部刷新的效果&#xff0c;那么springmvc中如何不返回頁面而返回頁面所需要的數據呢。 前后端數據交互現在大多使用json來表示(當然有一部分還是使用…

面試中的算法(查找缺失的整數)

在一個無序數組里有99個不重復的正整數&#xff0c;范圍是1~100&#xff0c;唯獨缺少1個1~100中的整數。如何找出這個缺失的整數? 一個很簡單也很高效的方法&#xff0c;先算出1~100之和&#xff0c;然后依次減去數組里的元素&#xff0c;最后得到的差值&#xff0c;就是那個缺…

目標檢測YOLO實戰應用案例100講-基于深度學習的無人機航拍圖像目標檢測算法研究與應用(中)

目錄 4.2旋轉角度 4.3數據集預處理 4.4旋轉框網絡結構設計 4.5實驗結果與分析

集合系列(二十五) -二叉樹、平衡二叉樹、紅黑樹性能總結

一、摘要 二叉樹&#xff0c;作為一種數據結構&#xff0c;在實際開發中&#xff0c;有著非常廣泛的應用&#xff0c;尤其是以平衡二叉樹、紅黑樹為代表&#xff0c;在前幾篇文章中&#xff0c;我們詳細的介紹了BST、AVL、RBT的算法以及代碼實踐&#xff0c;下面簡要概括描述一…

deveco studio 打開官方案例,不顯示運行按鈕。

就拿官方的search舉例好了 git 地址 https://gitee.com/harmonyos/samples/tree/master/ETSUI/Search 使用deveco studio打開Search項目&#xff0c;打開Tools->Device-Manager中的Local Emulator本地模擬器&#xff0c; 此時會發現&#xff0c;運行按鈕是灰色的&#xff0…

水利行業工程設計資質如何去申請

申請水利行業工程設計資質通常需要按照以下步驟進行&#xff1a; 事前準備&#xff1a; 制定材料清單&#xff0c;羅列出所需準備的文件。下載相關的申請表和模板。準備企業資料和人員資料等附件材料。人員要求&#xff1a; 確保企業擁有符合水利行業工程設計資質標準要求的注…

源碼 axios 的創建過程模擬實現

1、在實例對象上添加兩個屬性&#xff1a;default(默認配置) 與 interscptors // //構造函數function Axios(config) {//初始化this.defaults config;//為了創建 default 默認屬性this.interceptors {request: {},response: {}}} 2、在原型對象上添加方法 //原型添加相關的…

從零學算法994

994. 腐爛的橘子 在給定的 m x n 網格 grid 中&#xff0c;每個單元格可以有以下三個值之一&#xff1a; 值 0 代表空單元格&#xff1b; 值 1 代表新鮮橘子&#xff1b; 值 2 代表腐爛的橘子。 每分鐘&#xff0c;腐爛的橘子 周圍 4 個方向上相鄰 的新鮮橘子都會腐爛。 返回 直…

微信小程序中的數據可視化組件封裝藝術【附代碼】

微信小程序中的數據可視化組件封裝藝術 一、數據可視化的魅力與重要性數據可視化簡述為什么要在小程序中封裝數據可視化組件 二、微信小程序數據可視化基礎小程序中的繪圖工具&#xff1a;Canvas 三、實戰&#xff1a;封裝一個簡易折線圖組件設計思路組件結構&#xff08;line-…

java mybatis配置

MyBatis是一種支持自定義SQL、存儲過程和高級映射的持久層框架。下面是一個簡單的Java MyBatis配置示例&#xff1a; 首先&#xff0c;需要添加MyBatis的依賴到項目的pom.xml文件中&#xff1a; <dependency><groupId>org.mybatis</groupId><artifactId…

Python3 筆記:順序結構

三種程序執行結構&#xff1a;順序結構、選擇結構和循環結構。 這三種結構對應的是&#xff1a;順序執行所有的語句、選擇執行部分語句和循環執行部分語句。 順序結構是程序最基本的結構。就是程序按照語句順序&#xff0c;從上到下依次執行各條語句。 例如&#xff1a; nu…

【運維實踐項目|003】:Nginx集群化運維升級項目

項目名稱 項目簡稱或代號&#xff1a;SUN項目&#xff08;這個可以自己隨便編一個&#xff0c;每個公司的每個項目簡稱或代號都是內部任意起名的&#xff0c;顯得專業一點&#xff0c;一般是項目關鍵詞的首拼&#xff0c;比如這個CSUN是&#xff1a;ScaleUp Nginx&#xff09;…

一道dp錯題

dis(a,b)就是兩點之間的距離公式 那么這道題該怎么解呢,.先看數據范圍x,y<1e4,so,18個點兩點之間距離最大18*1e4*sqrt(2)<2^18,所以如果跳過的點大于18個點,那么顯然一個區間內最多不會跳躍超過17個點 現在我們想知道前i個點跳躍幾次在哪跳躍能夠達到最小花費,不妨設跳…

【OceanBase診斷調優】—— 轉儲錯誤(錯誤代碼 4138/ORA-01555)

當讀事務很長時&#xff0c;租戶進行轉儲會報 4138/ORA-01555 錯誤。本文介紹該錯誤的處理方法。 適用版本 OceanBase 數據庫 V2.X 及以后的版本 問題現象 當讀事務很長&#xff0c;租戶進行轉儲時會出現以下錯誤。 Oracle 租戶&#xff1a; ORA-01555&#xff1a;snapsho…

Keil調用跟蹤

調試時程序卡在一個位置&#xff0c;恰巧這個函數被很多地方調用&#xff0c;需要知道上一步在哪。 程序暫停后&#xff0c; 查看調用堆棧&#xff0c;點擊Keil菜單欄中的“View”&#xff0c;然后選擇“Call Stack”&#xff08;調用堆棧&#xff09;選項。這將顯示當前的調用…

市場活動系統搭建

精細差異化運營在今天的企業越來越普遍&#xff0c;運營驅動占據了業務經營的主導地位。各種營銷活動&#xff0c;幫助我們差異化運營、激發潛在客戶、帶動連帶消費、增加銷售額度、提升用戶增長、實現品牌宣傳。 天貓、京東上有各種各樣的促銷活動。如&#xff1a;滿減、滿返、…

算法day04

第一題 &#xff1a; 209. 長度最小的子數組 有上題可知&#xff0c;我們會采用雙指針和單調性的思路來解決 我們本題采用左右雙指針從數組的0位置同向前進&#xff0c;所以將此類模型稱為滑塊&#xff1b; 步驟思路如下&#xff1a; 步驟一&#xff1a; 定義所有雙指針都指向…

Prompt提示詞的技巧

Prompt提示詞的技巧 要讓GPT類模型產生最符合我們需求的輸出&#xff0c;我們需要精心設計和調整輸入的提示詞&#xff08;Prompt&#xff09;。 1、明確性&#xff1a; 確保你的提示詞清晰、具體。GPT類模型會根據你給出的信息來生成文本&#xff0c;因此&#xff0c;提供詳…