資料庫 SQL 入門(04):GROUP BY 分組與聚合——COUNT/AVG/SUM 到 HAVING 的完整心法

當你不只是「查資料」,而是要彙總出答案(每國家城市數、總人口、平均評分…),就會用到 GROUP BY 與一組聚合函數COUNT / SUM / AVG / MIN / MAX)。

本篇延續 COUNTRYCITY 範例,示範如何把原始列按鍵分組,再計算各組的統計,並以 HAVING分組後篩選結果(和分組前的 WHERE 差在時機點)。

SQL GROUP BY visualization showing data grouping concept

範例資料:國家表(COUNTRY)

idnamepopulationarea
1France66600000640680
2Germany80700000357000

這是我們將用於示範的國家資料表,包含國家的 ID、名稱、人口數量和面積資訊。

範例資料:城市表(CITY)

idnamecountry_idpopulationrating
1Paris122430005
2Berlin234600003

這是城市資料表,包含城市的 ID、名稱、所屬國家 ID、人口數量和評分資訊。我們將使用這兩個表來展示各種 SQL 查詢。

聚合與分組:GROUP BY

GROUP BY 子句將具有相同值的行分組在一起,並為每個唯一值組合計算摘要(聚合)。

原始 CITY 表

CITY
idnamecountry_id
1Paris1
101Marseille1
102Lyon1
2Berlin2
103Hamburg2
104Munich2
3Warsaw4
105Cracow4

按 country_id 分組後

country_idcount
13
23
42

聚合函數(Aggregate Functions)

AVG(expr)

計算組內行的平均值

COUNT(expr)

計算組內行的值數量

MAX(expr)

找出組內的最大值

MIN(expr)

找出組內的最小值

SUM(expr)

計算組內值的總和

聚合函數對一組行執行計算並返回單個值。它們通常與 GROUP BY 子句一起使用,但也可以應用於整個結果集。

graph showing minimum and maximum values with range between them

聚合查詢示例 – 基本計數

計算城市總數

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 函數分別返回列中的最小值和最大值。

這些函數對於了解數據範圍非常有用,可以幫助識別極值和異常值。

counting items in database with visual counter

聚合查詢示例 – 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 用熟,你就能把「一堆列」變成「能決策的指標」,是從查詢走向分析的必經步。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

返回頂端