GROUP BY 與 HAVING 分組統計
GROUP BY — 分組統計
比喻:GROUP BY 就像把學生按班級分組 📦
全校 300 個學生,你想知道「每班平均分數」—— 先把學生按班級分組,再對每組算平均。 這就是 GROUP BY 的概念。
基本用法
-- 每個科系有幾個學生?
SELECT Department, -- ← 分組的欄位
COUNT(*) AS 學生人數 -- ← 每組的統計
FROM Students
GROUP BY Department; -- ← 按 Department 分組
逐行解析:
SELECT Department -- 顯示科系名稱
COUNT(*) AS 學生人數 -- 每組有幾筆 = 每系幾人
FROM Students -- 從學生表
GROUP BY Department -- 把相同 Department 的資料歸為一組
-- 結果是:每組一列
結果:
Department | 學生人數
-----------+---------
資工系 | 15
電機系 | 12
企管系 | 20
搭配聚合函數
-- 每個科系的平均分數、最高分、最低分
SELECT
Department AS 科系,
COUNT(*) AS 人數,
ROUND(AVG(Score), 1) AS 平均分,
MAX(Score) AS 最高分,
MIN(Score) AS 最低分
FROM Students
GROUP BY Department
ORDER BY 平均分 DESC; -- ← 按平均分排序(高到低)
⚠️ GROUP BY 的重要規則
-- ❌ 錯誤:SELECT 裡的非聚合欄位,必須出現在 GROUP BY 裡
SELECT Department, Name, COUNT(*)
FROM Students
GROUP BY Department;
-- 錯誤!Name 不在 GROUP BY 裡,資料庫不知道要顯示哪個 Name
-- ✅ 正確:
SELECT Department, COUNT(*)
FROM Students
GROUP BY Department;
-- ✅ 或者把 Name 也加入 GROUP BY
SELECT Department, Name, COUNT(*)
FROM Students
GROUP BY Department, Name; -- 變成「每系 + 每人」一組
多欄分組
-- 按科系和性別分組
SELECT
Department AS 科系,
Gender AS 性別,
COUNT(*) AS 人數,
AVG(Score) AS 平均分
FROM Students
GROUP BY Department, Gender -- ← 兩個欄位一起分組
ORDER BY Department, Gender;
結果:
科系 | 性別 | 人數 | 平均分
-------+------+------+-------
資工系 | 男 | 10 | 78.5
資工系 | 女 | 5 | 82.3
電機系 | 男 | 8 | 75.0
電機系 | 女 | 4 | 80.1
HAVING — 過濾分組結果
WHERE 是在分組前過濾(過濾個別資料列) HAVING 是在分組後過濾(過濾分組結果)
-- 只顯示人數超過 10 的科系
SELECT Department, COUNT(*) AS 人數
FROM Students
GROUP BY Department
HAVING COUNT(*) > 10; -- ← 只要人數 > 10 的組
逐行解析:
GROUP BY Department -- 先分組
HAVING COUNT(*) > 10 -- 分完組之後,過濾掉人數 <= 10 的組
-- HAVING 裡只能用聚合函數或 GROUP BY 的欄位
WHERE vs HAVING
-- 找出「分數 >= 60 的學生中」每系平均超過 80 的科系
SELECT Department, AVG(Score) AS 平均分
FROM Students
WHERE Score >= 60 -- ① 先過濾:只看及格的學生
GROUP BY Department -- ② 再分組:按科系
HAVING AVG(Score) > 80 -- ③ 最後過濾組:只要平均 > 80
ORDER BY 平均分 DESC; -- ④ 排序結果
SQL 執行順序
1. FROM — 從哪個表
2. WHERE — 過濾個別列
3. GROUP BY — 分組
4. HAVING — 過濾分組
5. SELECT — 選欄位
6. ORDER BY — 排序
7. LIMIT — 取前 N 筆
💡 這個順序很重要!它解釋了為什麼 WHERE 不能用聚合函數——因為 WHERE 在 GROUP BY 之前執行,那時候還沒分組呢。
實用範例
-- 找出購買次數最多的前 5 個客戶
SELECT CustomerId, COUNT(*) AS 購買次數, SUM(Amount) AS 消費總額
FROM Orders
GROUP BY CustomerId
ORDER BY 購買次數 DESC
LIMIT 5;
-- 找出每月營業額超過 10 萬的月份
SELECT
DATE_TRUNC('month', OrderDate) AS 月份,
SUM(Amount) AS 月營業額
FROM Orders
GROUP BY DATE_TRUNC('month', OrderDate)
HAVING SUM(Amount) > 100000
ORDER BY 月份;