Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

網站會不定期發佈技術筆記、職場心得相關的內容,歡迎關注本站!

網站
首頁關於我部落格
部落格
分類系列文

© 新人日誌. All rights reserved. 2020-present.

本文為「SQL 資料庫新手村」系列第 37 篇

SQL SELECT 入門:什麼是聚合運算?

最後更新:2026年1月23日資料庫

前面學的 SELECT,都是針對「單筆資料」做運算。

比如說,計算每個商品的含稅價:

SELECT 名稱, 單價 * 1.05 AS 含稅價
FROM 商品

計算蘋果的含稅價時,只需要看蘋果自己的單價,不用管香蕉或牛奶。

但有些問題不是這樣。

比如說:「會員的平均收入是多少?」

這個問題沒辦法只看一筆資料就回答,必須把所有會員的收入加起來,再除以人數。

什麼是聚合運算?

這種「把很多筆資料放在一起,用一個值來代表」的運算,叫做聚合運算(Aggregate)。

為什麼叫「聚合」?

因為你把一堆資料「聚」在一起,「合」成一個結果。

舉個例子:台灣的平均月收入是 5 萬元。

這個「5 萬元」是怎麼來的?

是把全台灣所有人的月收入加起來,除以人數,得到一個數字。

這個數字「代表」了全台灣人的收入狀況。

當然,你可能會覺得「5 萬元不能代表我」,因為大多數人低於平均,是少數高薪的人拉高了平均。

這時候你可能會說:「應該用中位數比較準。」

但不管是平均數還是中位數,本質都一樣:把一堆資料放在一起,用一個值來代表這一群資料。

聚合運算的特性

這是很重要的觀念。

聚合運算的本質是:輸入很多筆資料,輸出一筆。

運算前運算後
100 筆會員資料1 個平均收入
1000 筆訂單1 個總金額
50 個學生成績1 個最高分
運算後1 個平均收入
運算後1 個總金額
運算後1 個最高分

不管原本有幾筆資料,聚合運算後都只剩下一筆。

記住這個特性,等一下會用到。

常見的聚合函數

在 SQL 中,聚合運算是透過「聚合函數」來實現的。

函數用途
COUNT()計算有幾筆資料
SUM()把所有數值加起來
AVG()計算平均值
MAX()找出最大值
MIN()找出最小值
用途計算有幾筆資料
用途把所有數值加起來
用途計算平均值
用途找出最大值
用途找出最小值

這些函數的共同點:輸入一整欄的資料,輸出一個數字。

找出收入高於平均的會員

老闆說:「請你找出收入高於平均的會員。」

聽起來很簡單,你可能會這樣寫:

SELECT *
FROM 會員
WHERE 收入 > AVG(收入)

WHERE 一次只能看一筆資料

看起來很合理,但這樣會報錯。

為什麼?

還記得前面學的嗎?WHERE 是一筆一筆檢查的。

假設會員表單有 100 筆資料,WHERE 會這樣做:

  1. 拿出第 1 筆(小明),檢查「小明的收入是否高於平均」→ 符合就留下,不符合就過濾
  2. 拿出第 2 筆(小華),檢查「小華的收入是否高於平均」→ 符合就留下,不符合就過濾
  3. …一直到第 100 筆

問題來了:當資料庫在檢查小明的時候,它只看小明這一筆資料。

假設小明的收入是 60000,資料庫會問:「60000 是否高於平均?」

但「平均收入」是多少?它不知道。

因為平均收入要這樣算:

平均收入 = (小明的收入 + 小華的收入 + 小美的收入 + ... + 第 100 個人的收入) ÷ 100

也就是說,你必須先看完全部 100 筆資料,才能算出平均收入。

但 WHERE 是一筆一筆檢查的。

檢查小明的時候,才剛開始,還沒看到小華、小美、還有後面 97 個人的資料。

既然還沒看完全部資料,就沒辦法算出平均收入。

既然不知道平均收入是多少,就沒辦法判斷「小明的收入是否高於平均」。

這就矛盾了:

  • 聚合運算(AVG)需要「看完全部 100 筆」才能算出結果
  • WHERE 一次只能「看 1 筆」

所以聚合函數不能直接放在 WHERE 裡面。

聚合函數會把資料「壓縮」成一筆

剛剛說過,聚合運算的特性是:輸入很多筆,輸出一筆。

換句話說,聚合函數會把資料「壓縮」。

100 筆會員資料,經過 AVG(收入) 之後,變成 1 個數字。

這會造成一個問題:壓縮後的結果,沒辦法和原本的資料放在一起。

你可能會想:既然 AVG 不能放在 WHERE,那我放在 SELECT 裡面,先算出平均收入,再拿來比較。

SELECT *, AVG(收入) AS 平均收入
FROM 會員
WHERE 收入 > 平均收入

這樣還是會報錯。

為什麼?

WHERE 比 SELECT 先執行。

還記得前面學過的執行順序嗎?

順序指令
1FROM
2WHERE
3SELECT
指令FROM
指令WHERE
指令SELECT

WHERE 在 SELECT 之前執行。

當 WHERE 在執行 收入 > 平均收入 的時候,SELECT 還沒執行。

SELECT 還沒執行,就代表 AVG(收入) AS 平均收入 這個計算還沒做。

計算還沒做,「平均收入」這個別名就還不存在。

所以 WHERE 會說:「平均收入是什麼?我不認識。」

壓縮後的筆數對不上。

SELECT * 是要輸出每一筆資料,100 筆就是 100 筆。

AVG(收入) 是把 100 筆壓縮成 1 筆。

假設你寫了 SELECT *, AVG(收入) AS 平均收入,資料庫會很困惑:

id姓名收入平均收入
1小明60000???
2小華40000???
3小美50000???
………???
100小王45000???
姓名小明
收入60000
平均收入???
姓名小華
收入40000
平均收入???
姓名小美
收入50000
平均收入???
姓名…
收入…
平均收入???
姓名小王
收入45000
平均收入???

「平均收入」這一欄應該填什麼?

AVG(收入) 的結果只有一個數字(假設是 50000),但表格有 100 列。

你可能會想:「資料庫應該夠聰明吧?就把 50000 填進每一格就好了啊。」

但資料庫不會這樣做。

為什麼?

因為 SELECT * 和 AVG(收入) 是兩種完全不同的操作:

  • SELECT *:把資料「一筆一筆列出來」
  • AVG(收入):把資料「全部壓縮成一筆」

這兩種操作的「方向」是相反的,一個要展開,一個要壓縮。

資料庫不會自作主張幫你決定「把 1 筆複製成 100 筆」,因為那樣做可能不是你要的結果。

所以資料庫選擇直接報錯,讓你明確告訴它:你到底要展開,還是要壓縮?

子查詢產生不了表單

你可能會想:那我用子查詢,先把平均收入算出來當成一個欄位,再篩選。

SELECT *
FROM (
    SELECT *, AVG(收入) AS 平均收入
    FROM 會員
) AS 子表單
WHERE 收入 > 平均收入

這樣還是會報錯。

為什麼?

還記得前面學過的嗎?SELECT 的本質是「輸出一個表單」,這個輸出的表單可以再當作另一個查詢的輸入。

子查詢就是利用這個原理:先用內層的 SELECT 產生一個表單,再讓外層的 SELECT 使用這個表單。

但問題是:內層的 SELECT *, AVG(收入) 本身就會報錯。

它根本產生不了一個正常的表單。

既然產生不了表單,外層的查詢自然也無法執行。

就像你想用一個壞掉的零件去組裝機器,不管你怎麼組,機器都不會動。

用子查詢回傳一個數字

換個思路。

平均收入是一個數字,假設是 50000。

那「收入高於平均」就等於「收入高於 50000」:

SELECT *
FROM 會員
WHERE 收入 > 50000

這樣寫當然可以,WHERE 在一筆一筆檢查的時候,比較的對象是一個固定的數字。

問題是:我們不知道平均收入是多少,要怎麼動態算出來?

答案是:用子查詢算出平均收入,它的結果就是一個數字。

SELECT *
FROM 會員
WHERE 收入 > (SELECT AVG(收入) FROM 會員)

為什麼這樣可以?

前面不是說「SELECT *, AVG(收入) 會報錯」嗎?這裡怎麼又可以了?

關鍵差異在於:

寫法SELECT 輸出的筆數結果
SELECT *, AVG(收入)100 筆 + 1 筆矛盾,報錯
SELECT AVG(收入)只有 1 筆沒有矛盾,成功
SELECT 輸出的筆數100 筆 + 1 筆
結果矛盾,報錯
SELECT 輸出的筆數只有 1 筆
結果沒有矛盾,成功

SELECT *, AVG(收入) 會報錯,是因為 SELECT * 要輸出 100 筆,AVG(收入) 只有 1 筆,數量對不上。

但 SELECT AVG(收入) 不一樣。

它只做一件事:把所有收入壓縮成一個平均值。

沒有 SELECT *,就沒有「100 筆 vs 1 筆」的矛盾。

所以 (SELECT AVG(收入) FROM 會員) 可以順利執行,產生一個數字,假設是 50000。

那整個查詢就等於:

SELECT *
FROM 會員
WHERE 收入 > 50000

WHERE 在一筆一筆檢查的時候,比較的對象是一個已經算好的數字,不是一個「還要去算」的聚合函數。

這樣就不會報錯了!

小結

這篇文章介紹了聚合運算:

  1. 聚合運算:把很多筆資料放在一起,用一個值來代表
  2. 多筆變一筆:這是聚合運算最重要的特性
  3. 不能放在 WHERE:因為 WHERE 是一筆一筆檢查的
  4. 不能和一般欄位混用:一筆一筆和聚合互相矛盾
  5. 用子查詢繞過:讓子查詢回傳一個數字,就可以放在 WHERE 裡比較

之後還會學到用 GROUP BY 來做分組聚合,把「多筆變一筆」變成「多筆變多組,每組一筆」。

上一篇SQL SELECT 入門:用 LIMIT 和 OFFSET 限制筆數
下一篇SQL SELECT 入門:用 GROUP BY 把資料分組
目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

資料庫

目錄

  • 什麼是聚合運算?
  • 聚合運算的特性
  • 常見的聚合函數
  • 找出收入高於平均的會員
  • WHERE 一次只能看一筆資料
  • 聚合函數會把資料「壓縮」成一筆
  • 子查詢產生不了表單
  • 用子查詢回傳一個數字
  • 小結