當你不只是「查資料」,而是要彙總出答案(每國家城市數、總人口、平均評分…),就會用到 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_rating
FROM city WHERE rating IS NOT NULL
GROUP 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 用熟,你就能把「一堆列」變成「能決策的指標」,是從查詢走向分析的必經步。