前言
GROUP BY 是 SQL 聚合分析的基石,配合聚合函數可將明細資料轉為摘要報表。本文從基礎語法切入,透過「遊戲統計」與「勝負記錄」兩大實戰案例,詳細講解單欄分組、多欄分組、條件聚合、PHP 整合等核心技巧。
核心語法結構
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
column_name:分組依據欄位(可多欄,用逗號分隔)。
aggregate_function:SUM、COUNT、AVG、MAX、MIN 等。
WHERE:分組前過濾;HAVING:分組後過濾。
案例一:遊戲數據多欄聚合
原始資料
| gameid | best | top | spam |
| 111 | 1 | 1 | 1 |
| 121 | 1 | 1 | 0 |
| 123 | 0 | 1 | 1 |
| 111 | 1 | 0 | 1 |
目標結果
| gameid | best | top | spam |
| 111 | 2 | 1 | 2 |
| 121 | 1 | 1 | 0 |
| 123 | 0 | 1 | 1 |
SQL 實作
SELECT gameid,
SUM(best) AS bestcnt,
SUM(top) AS topcnt,
SUM(spam) AS spamcnt
FROM game_table
GROUP BY gameid;
PHP 整合範例
$sql = "SELECT gameid,
SUM(best) AS best,
SUM(top) AS top,
SUM(spam) AS spam
FROM tablename
GROUP BY gameid
ORDER BY gameid ASC";
案例二:勝負記錄橫向轉置(條件聚合)
原始資料
| 日期 | 勝負 |
| 2009-12-09 | 勝 |
| 2009-12-09 | 勝 |
| 2009-12-09 | 負 |
| 2009-12-09 | 負 |
| 2009-12-10 | 負 |
| 2009-12-10 | 勝 |
| 2009-12-10 | 負 |
目標結果
| 日期 | 勝 | 負 |
| 2009-12-09 | 2 | 2 |
| 2009-12-10 | 1 | 2 |
Oracle 寫法(DECODE)
SELECT 日期,
SUM(DECODE(勝負, '勝', 1, 0)) AS 勝,
SUM(DECODE(勝負, '負', 1, 0)) AS 負
FROM 數據庫表
GROUP BY 日期;
SQL Server / MySQL 通用寫法(CASE WHEN)
SELECT 日期,
SUM(CASE WHEN 勝負 = '勝' THEN 1 ELSE 0 END) AS 勝,
SUM(CASE WHEN 勝負 = '負' THEN 1 ELSE 0 END) AS 負
FROM 數據庫表
GROUP BY 日期;
進階技巧與常見陷阱
- SELECT 清單限制:未出現在
GROUP BY 的非聚合欄位不可直接出現在 SELECT 中(MySQL ONLY_FULL_GROUP_BY 模式會報錯)。
- 多欄分組:
GROUP BY dept_id, job_id 依部門再依職務分組。
- ROLLUP / CUBE:產生小計與總計列(SQL Server/Oracle 支援)。
- HAVING 過濾分組:
HAVING COUNT(*) > 10 只保留筆數大於 10 的群組。
- NULL 值處理:
GROUP BY 會將 NULL 視為一個獨立群組;可用 COALESCE(col, '未知') 重新歸類。
效能優化建議
- 為
GROUP BY 欄位建立索引(複合索引順序與分組順序一致)。
- 先用
WHERE 縮減資料量,再分組聚合。
- 大數據量考慮分區表或物化視圖預聚合。
總結
熟練掌握 GROUP BY 與條件聚合(CASE WHEN/DECODE),即可輕鬆完成從簡單統計到複雜交叉表的轉換。建議開發者建立「分組聚合模板庫」,加速報表開發並減少語法錯誤。