GROUP BY 是面試高頻考點,考察對分組聚合、非聚合列選擇、HAVING vs WHERE、ONLY_FULL_GROUP_BY 模式的理解。
CREATE TABLE student (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30), sex TINYINT, score INT, dept VARCHAR(10)
);
INSERT INTO student VALUES
(1,'a',1,90,'dev'),(2,'b',1,90,'dev'),(3,'b',0,88,'design'),
(4,'c',0,60,'sales'),(5,'c',0,89,'sales'),(6,'d',1,100,'product');
SELECT dept, MAX(score) AS max_score
FROM student GROUP BY dept;
結果:每部門最高分。注意:未分組列(name, sex, score)在 SELECT 中出現時,MySQL 非 ONLY_FULL_GROUP_BY 模式下任意取值,嚴格模式下報錯。
-- 部門總分 > 150 的最高分
SELECT dept, MAX(score) FROM student
GROUP BY dept HAVING SUM(score) > 150;
-- 僅男生(sex=1)參與分組的最高分
SELECT dept, MAX(score) FROM student
WHERE sex = 1 GROUP BY dept;
-- 或
SELECT dept, MAX(score) FROM student
GROUP BY dept HAVING sex = 1; -- 非標準,依賴 ONLY_FULL_GROUP_BY=OFF
核心區別:WHERE 在分組前過濾行;HAVING 在分組後過濾組,可用聚合函數。
-- 僅獲取不重複部門
SELECT DISTINCT dept FROM student;
-- 獲取部門及任一學生名(非標準)
SELECT dept, name FROM student GROUP BY dept;
嚴格模式(ONLY_FULL_GROUP_BY,5.7+ 默認開啟)要求 SELECT 列必須出現在 GROUP BY 或聚合函數中。解決方案:
MAX(name)、ANY_VALUE(name)(5.7+)。每部門最高分的學生信息(MySQL 8.0 窗口函數):
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY score DESC) AS rn
FROM student
) t WHERE rn = 1;
5.7 兼容寫法(變量模擬行號或自連接)。
| 陷阱 | 錯誤寫法 | 正確寫法 |
|---|---|---|
| 非聚合列任意值 | SELECT dept, name FROM t GROUP BY dept | SELECT dept, ANY_VALUE(name) FROM t GROUP BY dept |
| WHERE 用聚合函數 | SELECT dept FROM t WHERE COUNT(*) > 5 GROUP BY dept | SELECT dept FROM t GROUP BY dept HAVING COUNT(*) > 5 |
| HAVING 無 GROUP BY | SELECT * FROM t HAVING score > 90 | SELECT * FROM t WHERE score > 90 |
| 分組字段與 SELECT 不一致 | SELECT dept, MAX(score) FROM t GROUP BY name | SELECT dept, MAX(score) FROM t GROUP BY dept |
-- 查看當前模式
SELECT @@sql_mode;
-- 臨時關閉(不推薦生產)
SET SESSION sql_mode = REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', '');
建議代碼兼容嚴格模式,避免升級後報錯。
面試核心考點:WHERE vs HAVING 執行順序、非聚合列處理、ONLY_FULL_GROUP_BY 語義、分組 Top-N 寫法。熟練掌握上述場景,GROUP BY 題目穩拿高分。