當你不只是「查資料」,而是要彙總出答案(每國家城市數、總人口、平均評分…),就會用到 GROUP BY 與一組聚合函數(COUNT / SUM / AVG / MIN / MAX)。
本篇延續 COUNTRY/CITY 範例,示範如何把原始列按鍵分組,再計算各組的統計,並以 HAVING 在分組後篩選結果(和分組前的 WHERE 差在時機點)。
範例資料:國家表(COUNTRY)
| id | name | population | area |
| 1 | France | 66600000 | 640680 |
| 2 | Germany | 80700000 | 357000 |
| … | … | … | … |
這是我們將用於示範的國家資料表,包含國家的 ID、名稱、人口數量和面積資訊。
範例資料:城市表(CITY)
| id | name | country_id | population | rating |
| 1 | Paris | 1 | 2243000 | 5 |
| 2 | Berlin | 2 | 3460000 | 3 |
| … | … | … | … | … |
這是城市資料表,包含城市的 ID、名稱、所屬國家 ID、人口數量和評分資訊。我們將使用這兩個表來展示各種 SQL 查詢。
聚合與分組:GROUP BY
GROUP BY 子句將具有相同值的行分組在一起,並為每個唯一值組合計算摘要(聚合)。
原始 CITY 表
| CITY | ||
| id | name | country_id |
| 1 | Paris | 1 |
| 101 | Marseille | 1 |
| 102 | Lyon | 1 |
| 2 | Berlin | 2 |
| 103 | Hamburg | 2 |
| 104 | Munich | 2 |
| 3 | Warsaw | 4 |
| 105 | Cracow | 4 |
按 country_id 分組後
| country_id | count |
| 1 | 3 |
| 2 | 3 |
| 4 | 2 |
聚合函數(Aggregate Functions)
AVG(expr)
計算組內行的平均值
COUNT(expr)
計算組內行的值數量
MAX(expr)
找出組內的最大值
MIN(expr)
找出組內的最小值
SUM(expr)
計算組內值的總和
聚合函數對一組行執行計算並返回單個值。它們通常與 GROUP BY 子句一起使用,但也可以應用於整個結果集。
聚合查詢示例 – 基本計數
計算城市總數
SELECT COUNT(*)
FROM city;
返回 city 表中的行數
計算非空評分的城市數
SELECT COUNT(rating)
FROM city;
返回 city 表中 rating 列非空的行數
計算不同國家的數量
SELECT COUNT(DISTINCT country_id)
FROM city;
返回 city 表中不同 country_id 值的數量
聚合查詢示例 – MIN 和 MAX
SELECT MIN(population), MAX(population)
FROM country;
此查詢找出國家人口的最小值和最大值。MIN 和 MAX 函數分別返回列中的最小值和最大值。
這些函數對於了解數據範圍非常有用,可以幫助識別極值和異常值。
聚合查詢示例 – GROUP BY
按國家分組的城市總人口
SELECT country_id, SUM(population)
FROM city
GROUP BY country_id;
計算每個國家的城市總人口
帶 HAVING 子句的分組
SELECT country_id, AVG(rating)
FROM city
GROUP BY country_id
HAVING AVG(rating) > 3.0;
找出平均評分高於 3.0 的國家
GROUP BY 將行分組,而 HAVING 過濾分組結果。HAVING 與 WHERE 類似,但用於過濾分組後的結果,而 WHERE 過濾分組前的行。
總結
- 聚合基本功
- 全表計數:
COUNT(*)(包含NULL列)。 - 非空計數:
COUNT(column)(忽略NULL)。 - 不重複計數:
COUNT(DISTINCT column);多欄去重多數資料庫支援COUNT(DISTINCT col1, col2)(支援度視系統)。
- 全表計數:
- GROUP BY 規則
SELECT中所有非聚合欄位都必須出現在GROUP BY(少數系統關掉嚴格模式例外,不建議依賴)。NULL會被歸為同一組(形成一個「NULL 組」)。- 可多鍵分組:
GROUP BY country_id, rating。
- WHERE 與 HAVING 的時機
- WHERE:在分組前先縮小資料集(可用索引、效能佳)。
- HAVING:在分組後過濾聚合結果:
SELECT country_id, AVG(rating) AS avg_ratingFROM city WHERE rating IS NOT NULLGROUP BY country_id HAVING AVG(rating) > 3.0;
- 常見寫法
- 各國家城市總人口:
SELECT country_id, SUM(population) FROM city GROUP BY country_id; - 所有城市數:
SELECT COUNT(*) FROM city; - 有評分的城市數:
SELECT COUNT(rating) FROM city; - 不同國家數:
SELECT COUNT(DISTINCT country_id) FROM city; - 全表極值:
SELECT MIN(population), MAX(population) FROM country;
- 各國家城市總人口:
- 實務與效能
- 先用 WHERE 縮小資料再分組;為分組鍵/篩選鍵建立索引。
- 需要排序結果時加
ORDER BY(例如ORDER BY SUM(population) DESC)。 - 數值型別留意:
AVG回傳型別依資料庫而異;避免整數除法誤差可CAST或確保小數型別。 - 大量
COUNT(DISTINCT …)可能昂貴;必要時考慮近似算法或預彙總表。
- 易踩的坑
- 在
SELECT引用未分組且未聚合的欄位。 - 用
HAVING取代可用WHERE的條件(會慢)。 - 忘了處理
NULL帶來的「空組」影響平均與計數。
- 在
把 GROUP BY + 聚合 + HAVING 用熟,你就能把「一堆列」變成「能決策的指標」,是從查詢走向分析的必經步。