GROUP BY 語句根據一個或多個字段對結果集進行分組(也就是把值相同放到一個組中,顯示組中一條記錄),實作對每個組而不是對整個結果集統計。SELECT子句中的列名必須為分組列、聚合函數或常量。
文法形式為:
SELECT [field1, field12, ..., ] fun_name
FROM tablename
JOIN ON join_condition
WHERE where_conditon
GROUP BY field1, field12, ..., fieldn
[WITH ROLLUP]
[HAVING having_condition]
[ORDER BY field1, field12, ..., fieldn [ASC | DESC]]
參數說明:
SELECT 子句中的列必須為分組列(寫在GROUP BY 後面的列)、聚合函數(SUM、MAX、AVG等)或常量(MySql5.7以後版本,預設是開啟了only_full_group_by 模式的)。
fun_name 表示聚合函數,常見的有:SUM(總和)、AVG(平均值)、COUNT(行數)、MAX(最大值)、MIN(最小值)、GROUP_CONCAT(串聯的字元串)、JSON_ARRAYAGG(将結果集作為單個JSON數組傳回)、JSON_OBJECTAGG(将結果集作為單個JSON對象傳回)。
GROUP BY 表示一個或多個字段對結果集進行分組,比如按照部門統計人數。
WITH ROLLUP 是可選的,表示是否對分組後的結果再進行統計。
HAVING 表示對分組的結果再進行條件過濾,比如統計部門人數大于10人的部門。(HAVING和WHERE的差別,HAVING 是對分組後的結果進行條件的過濾,而WHERE是在分組前進行條件的過濾,如果可以,我們應盡可能用WHERE先過濾記錄,這樣結果集減少,提高分組效率。)
GROUP BY用法舉例
我們以MySql示例employees庫示範,employees庫表描述可以參考:mysql示例employees資料庫安裝和介紹
統計公司的總人數
SELECT COUNT(1) FROM employees ;

注意:使用count(列名)當某列出現null值的時候,count(*)仍然會計算,但是count(列名)不會。
統計各個部門人數
SELECT `dept_no`,COUNT(1) FROM `dept_emp` GROUP BY `dept_no`;
通過統計公司總人數和統計各個部門人數,我們能看到GROUP BY 作用,實作對每個組而不是對整個結果集統計。
在對結果集資料分組以後,SELECT 子句中的列必須為分組列、聚合函數(SUM、MAX、AVG等)或常量。如果我們在統計部門人數增加沒有出現在GROUP BY後面列emp_no(員工id),會怎樣?
報錯了:列emp_no不在GROUP BY子句中,并且也不是聚合函數,這與sql_mode=only_full_group_by 不相容(在MySql5.7以後版本,預設開啟了only_full_group_by 模式的,以前的版本沒有開啟only_full_group_by 模式,不會報錯)。
為什麼分組後不能再引用原表中的列(不在 GROUP BY 子句的列)?
GROUP BY的作用是将表中結果集分組, GROUP BY 操作的對象是由多個員工組成的小組,小組的屬性隻能是SUM、AVG、MAX等統計的屬性,而emp_no(員工id)是每個員工屬性,并不是小組的屬性。
假如分組後分組的某個非分組的列值都一樣,或者說隻想随機選擇一個列值,那該怎麼操作?有兩種方法:
1、使用ANY_VALUE()函數
SELECT ANY_VALUE(`emp_no`) `emp_no`, `dept_no`,COUNT(1) FROM `dept_emp` GROUP BY `dept_no`;
注意,如果是在MySql5.7版本以下使用ANY_VALUE()函數會報錯(代碼示範的MySql5.5版本)。
2、關閉only_full_group_by 模式
通過select @@sql_mode查出sql_mode,去掉ONLY_FULL_GROUP_BY後複制過來
SELECT @@sql_mode;
修改my.cnf檔案,在mysqld下面增加
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
然後重新開機MySql服務
建議不要在SELECT子句中引用不在GROUP BY子句中列,它會傳回一個不确定的值,這可能不是您想要的。
GROUP_CONCAT()函數使用
作用将group by産生的同一個分組中的值連接配接起來,傳回一個字元串結果。
文法group_concat( [distinct] 要連接配接的字段 [order by 排序字段 asc/desc ] [separator '分隔符'] )。
統計每個部門曆任經理,并按照當經理時間從前到後排序
SELECT GROUP_CONCAT(`emp_no` ORDER BY `from_date` ASC SEPARATOR ',') `emp_no`, `dept_no` FROM `dept_manager` GROUP BY `dept_no`;
說明:
group_concat長度:group_concat預設最大長度是1024,超過截取前1024長度字元。
查詢group_concat:show variables like 'group_concat_max_len';
修改group_concat:修改配置檔案my.conf(需要重新開機mysql服務) group_concat_max_len = 2048;
WITH ROLLUP子句實作在分組統計資料基礎上再進行相同的統計
既統計部門人數,也統計所有部門總人數
SELECT `dept_no`, COUNT(1) FROM `dept_emp` GROUP BY `dept_no` WITH ROLLUP;
WITH ROLLUP子句可以實作在分組統計資料基礎上再進行相同的統計,像上面的例子,統計所有部門總人數,NULL 331603,如何把字段WITH ROLLUP彙總字段NULL改成total,方法有很多,比如使用IFNULL(expr1,expr2)、IF(expr1,expr2,expr3)、GROUPING(expr [, expr] ...)、COALESCE(expr1,expr2,expr3,....,exprn)函數。
下面例子我們使用COALESCE()函數
COALESCE(a, b, c, ...., n) 作用是将傳回傳入的參數中第一個非null的值。
SELECT COALESCE(`dept_no`,'total') `dept_no`, COUNT(1) FROM `dept_emp` GROUP BY `dept_no` WITH ROLLUP;
使用ROLLUP時的其他注意事項
在MySQL 8.0.12之前,使用時ROLLUP,不能同時使用ORDER BY子句對結果進行排序。
SELECT COALESCE(`dept_no`,'total') `dept_no`, COUNT(1) FROM `dept_emp` GROUP BY `dept_no` WITH ROLLUP ORDER BY COUNT(1) DESC;
但是你可以采用子查詢的辦法實作排序
SELECT * FROM (SELECT COALESCE(`dept_no`,'total') `dept_no`, COUNT(1) `cnt` FROM `dept_emp` GROUP BY `dept_no` WITH ROLLUP) AS `sub` ORDER BY `cnt` DESC;
從MySQL 8.0.12開始,ORDER BY并且 ROLLUP可以一起使用
SELECT COALESCE(`dept_no`,'total') `dept_no`, COUNT(1) FROM `dept_emp` GROUP BY `dept_no` WITH ROLLUP ORDER BY COUNT(1) DESC;
要注意:與SELECT子句列一樣,ORDER BY子句的列也必須為分組列、聚合函數(SUM、MAX、AVG等)。
HAVING 子句對分組結果進行篩選
統計2000年以後進入部門且數量大于500人的部門
SELECT `dept_no`, COUNT(1) `cnt` FROM `dept_emp` WHERE Year(`from_date`) > 2000 GROUP BY `dept_no` HAVING `cnt` > 500;
WHERE和HAVING差別
WHERE在查詢傳回之前對資料庫中的查詢條件進行過濾,即在結果傳回之前起作用,且where後面不能使用聚合函數,WHERE不可以用字段别名(where後面不能使用聚合函數是因為where的執行順序在聚合函數之前)。
HAVING 在查詢資料庫的結果分組之後進行過濾,即在結果傳回之後起作用,是以HAVING 後面可以使用聚合函數,HAVING子句可以使用字段别名。