前言
聚合函數是資料分析的基石。本文以 tb_Students (學生) 與 tb_Class (班級) 兩張範例表,完整演示五大基本聚合函數、分組聚合、去重聚合與實戰查詢模式。
一、範例資料準備
-- 學生表
CREATE TABLE tb_Students (
id INT IDENTITY PRIMARY KEY,
name NVARCHAR(16) NOT NULL,
age INT NOT NULL
);
INSERT INTO tb_Students (name, age) VALUES
(N'tiana', 15), (N'yardi', 20), (N'zhangyun', 18);
-- 班級表
CREATE TABLE tb_Class (
classid INT IDENTITY PRIMARY KEY,
classname NVARCHAR(16) NOT NULL,
students INT NOT NULL,
teacher NVARCHAR(16) NOT NULL
);
INSERT INTO tb_Class (classname, students, teacher) VALUES
(N'1班', 25, N'yl'), (N'2班', 18, N'wsp'), (N'3班', 24, N'yl'),
(N'4班', 25, N'zhy'), (N'5班', 22, N'wsp'), (N'6班', 25, N'yl'),
(N'7班', 20, N'zhy'), (N'8班', 22, N'wsp');
二、五大基本聚合函數
| 函數 | 用途 | 查詢範例 (學生年齡 > 15) | 結果 |
| SUM | 加總 | SELECT SUM(age) FROM tb_Students WHERE age > 15; | 38 (20+18) |
| COUNT | 計數 | SELECT COUNT(*) FROM tb_Students WHERE age > 15; | 2 |
| AVG | 平均 | SELECT AVG(age) FROM tb_Students WHERE age > 15; | 19 |
| MAX | 最大值 | SELECT MAX(age) FROM tb_Students WHERE age > 15; | 20 |
| MIN | 最小值 | SELECT MIN(age) FROM tb_Students WHERE age > 15; | 18 |
COUNT 細節:COUNT(*) 計算列數 (含 NULL);COUNT(col) 計算非 NULL 個數;COUNT(DISTINCT col) 計算非 NULL 去重個數。本例三欄位無 NULL,結果一致。
三、GROUP BY 分組聚合
-- 依教師分組,計算各教師班級學生數統計
SELECT
teacher,
SUM(students) AS TotalStudents,
COUNT(*) AS ClassCount,
AVG(students * 1.0) AS AvgClassSize,
MAX(students) AS MaxClassSize,
MIN(students) AS MinClassSize
FROM tb_Class
GROUP BY teacher;
結果
teacher TotalStudents ClassCount AvgClassSize MaxClassSize MinClassSize
yl 74 3 24.666... 25 24
wsp 62 3 20.666... 25 18
zhy 45 2 22.5 25 20
四、DISTINCT 去重聚合
-- 查詢有多少位不同的教師
SELECT COUNT(DISTINCT teacher) FROM tb_Class;
-- 結果: 3 (yl, wsp, zhy)
DISTINCT 放在聚合函數**括號內**,先去重再聚合。
- 可用於
SUM(DISTINCT col)、AVG(DISTINCT col) 等,但極少用於業務邏輯 (意義模糊)。
五、進階實戰模式
5.1 HAVING 篩選分組結果
-- 找出總學生數超過 60 人的教師
SELECT teacher, SUM(students) AS Total
FROM tb_Class
GROUP BY teacher
HAVING SUM(students) > 60;
WHERE vs HAVING:WHERE 篩選**原始列** (聚合前);HAVING 篩選**分組彙總結果** (聚合後)。
5.2 多欄位分組 + ROLLUP 小計
SELECT
teacher,
classname,
SUM(students) AS Total
FROM tb_Class
GROUP BY ROLLUP (teacher, classname);
-- 產生:各班級小計 → 各教師小計 → 總計
5.3 聚合函數忽略 NULL
SUM/AVG/MIN/MAX 自動忽略 NULL 值。
COUNT(col) 忽略 NULL;COUNT(*) 不忽略。
5.4 條件聚合 (CASE 結合聚合)
SELECT
teacher,
SUM(CASE WHEN classname LIKE '1%' THEN students ELSE 0 END) AS Class1_Sum,
SUM(CASE WHEN students >= 25 THEN 1 ELSE 0 END) AS LargeClassCount
FROM tb_Class
GROUP BY teacher;
六、常見錯誤與避坑
| 錯誤 | 原因 | 修正 |
SELECT name, SUM(age) FROM tb_Students | 非聚合欄位 name 未出現在 GROUP BY | 加入 GROUP BY name 或移除 name |
SELECT AVG(SUM(age)) ... | **不可巢狀聚合函數** | 用子查詢/CTE 分層:SELECT AVG(Total) FROM (SELECT SUM(age) AS Total FROM ... GROUP BY ...) t |
WHERE SUM(age) > 100 | WHERE 不可用聚合函數 | 改用 HAVING SUM(age) > 100 |
| 整數除法導致 AVG 截斷 | AVG(INT) 回傳 INT | AVG(age * 1.0) 或 CAST(AVG(age * 1.0) AS DECIMAL(10,2)) |
七、效能提示
- 聚合欄位**建立索引** (含分組欄位) 可用 Stream Aggregate 避免 Sort。
- 大表分組聚合考慮 **Columnstore Index** (SQL 2012+),批次模式處理極快。
- 避免
DISTINCT 濫用;若業務允許,改用 GROUP BY 更可控。