首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL GROUP BY 與 HAVING 進階用法:非標準行為解析與最佳實踐

     編輯:MySQL 綜合教學

核心問題:MySQL 允許 SELECT 列表包含非 GROUP BY 且非聚合的欄位

標準 SQL 要求 SELECT 列表中所有非聚合欄位必須出現在 GROUP BY 中。但 MySQL 預設啟用 ONLY_FULL_GROUP_BY 關閉時,允許此類查詢並任意返回分組內第一行的值(非確定性)。

實驗驗證

-- 建表與測試資料
CREATE TABLE t (id INT, name VARCHAR(100), aa VARCHAR(45));
INSERT INTO t VALUES
(1,'aaaa','bbbb'), (1,'1111','2222'), (1,'2222','33333'),
(1,'2222','44444'), (2,'2222','44444'), (2,'2222','1111'),
(3,'2222','1111'), (1,'2222','44444'), (1,'2222','44444'),
(1,'2222','44444'), (3,'2222','aaaa');

-- 僅 GROUP BY id,但 SELECT 包含 aa
SELECT id, COUNT(1), aa FROM t GROUP BY id;

結果:aa 顯示各組第一行的值(如 id=1 顯示 'bbbb'),COUNT(1) 正確。此行為依賴儲存引擎與索引順序,不具備可移植性,生產環境嚴禁依賴。

標準寫法與 ONLY_FULL_GROUP_BY

MySQL 5.7+ 預設開啟 ONLY_FULL_GROUP_BY,強制標準行為。正確寫法:

-- 方案 1:所有非聚合欄位加入 GROUP BY
SELECT id, aa, COUNT(1) FROM t GROUP BY id, aa;

-- 方案 2:使用聚合函數明確語義
SELECT id, ANY_VALUE(aa), COUNT(1) FROM t GROUP BY id;

HAVING 子句正確用法

HAVING 用於過濾分組後的聚合結果,WHERE 無法使用聚合函數。

-- 查詢總分 > 1000 的學生
SELECT student, SUM(score) AS total
FROM scores
GROUP BY student
HAVING total > 1000;

-- 結合 WHERE(行過濾)與 HAVING(組過濾)
SELECT dept, AVG(salary) AS avg_sal
FROM employees
WHERE hire_date > '2020-01-01'
GROUP BY dept
HAVING avg_sal > 50000;

常見錯誤與避坑指南

錯誤寫法問題修正
SELECT name, SUM(pt) FROM t GROUP BY name HAVING pt > 100pt 非聚合且不在 GROUP BYHAVING SUM(pt) > 100
SELECT id, name FROM t GROUP BY id非確定性結果加入 name 或用 ANY_VALUE(name)
用 HAVING 替代 WHERE 做非聚合過濾效率低,語義不清行級過濾用 WHERE

總結

  • 生產環境務必開啟 ONLY_FULL_GROUP_BY,編寫標準相容 SQL。
  • GROUP BY 定義分組維度,HAVING 過濾聚合結果,兩者分工明確。
  • 避免依賴 MySQL 非標準擴展,確保遷移相容性與結果確定性。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved