Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

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

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

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

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

SQL SELECT 入門:用 GROUP BY 把資料分組

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

前面學過聚合運算,可以把整張表的資料「壓縮」成一筆。

但很多時候,你不只想知道「全部加起來是多少」。

比如說,老闆問你:「每個部門有幾個員工?」

這時候就需要用 GROUP BY 來「分組」。

什麼是分組?

分組就是把資料按照某個「類別」分成一堆一堆。

比如說,這個世界上的人可以按照:

  • 國籍分:台灣人、日本人、美國人⋯⋯
  • 血型分:A 型、B 型、O 型、AB 型

同一個類別的人會被歸到同一堆。

在資料庫裡,這個「類別」就是欄位。

比如說,員工表裡有一個「部門」欄位,值可能是「行銷」、「工程」、「人資」。

如果你想把員工按照部門分成一堆一堆,語法就是:

GROUP BY 部門

這樣行銷部的員工會被歸成一堆、工程部的員工一堆、人資部的員工一堆。

用 GROUP BY 找出不重複的資料

了解分組的概念之後,來看一個實際的例子。

假設我們有一張訂單表:

訂單編號會員金額
1Alice100
2Alice200
3Bob150
4Alice300
5Bob250
會員Alice
金額100
會員Alice
金額200
會員Bob
金額150
會員Alice
金額300
會員Bob
金額250

老闆說:「幫我找出有下過單的會員有哪些。」

你可能會這樣寫:

SELECT 會員
FROM 訂單

結果會是:

會員
Alice
Alice
Bob
Alice
Bob

但這樣會有問題:Alice 出現了 3 次,Bob 出現了 2 次。

老闆要的是「不重複的會員名單」,不是一堆重複的名字。

怎麼做到「不重複」?

用 GROUP BY:

SELECT 會員
FROM 訂單
GROUP BY 會員

為什麼這樣可以做到不重複?

因為 GROUP BY 會員,會把「會員欄位值相同的」放在一起:

組別訂單編號會員金額
第一組1Alice100
第一組2Alice200
第一組4Alice300
第二組3Bob150
第二組5Bob250
訂單編號1
會員Alice
金額100
訂單編號2
會員Alice
金額200
訂單編號4
會員Alice
金額300
訂單編號3
會員Bob
金額150
訂單編號5
會員Bob
金額250

Alice 的 3 筆變成一組、Bob 的 2 筆變成一組。

然後把每一組「壓縮」成一列。

因為我們寫的是 GROUP BY 會員,所以資料庫會用「會員」這個欄位來壓縮。

看看第一組,有 3 筆訂單:

訂單編號會員金額
1Alice100
2Alice200
4Alice300
會員Alice
金額100
會員Alice
金額200
會員Alice
金額300

壓縮成一列之後,會變成什麼?

  • 會員欄位:這是 GROUP BY 指定的欄位,3 筆都是 Alice,壓縮後輸出 Alice。
  • 訂單編號欄位:不是 GROUP BY 指定的欄位,3 筆分別是 1、2、4,壓縮成一列要填什麼?1?2?4?還是加起來變成 7?資料庫不知道你要什麼,所以不讓你直接用。
  • 金額欄位:同樣不是 GROUP BY 指定的欄位,資料庫也不讓你直接用。

所以壓縮之後,只有 GROUP BY 指定的欄位可以直接用,其他欄位沒辦法直接 SELECT。

結果就是:

會員
Alice
Bob

這是 GROUP BY 壓縮後的結果。

接下來輪到 SELECT 上場。

SELECT 會員,就是告訴資料庫:「從壓縮後的結果中,把會員欄位顯示出來。」

因為壓縮後只剩會員欄位可以用,所以 SELECT 也只能選會員:

會員
Alice
Bob

如果你硬要 SELECT 訂單編號或金額,資料庫會報錯,因為這些欄位在壓縮後已經不存在了。

這就是 GROUP BY 在做的事:先分組,再壓縮。

GROUP BY 之後,看不到個別資料

老闆又說:「那順便把每筆訂單的金額也列出來。」

你可能會這樣寫:

SELECT 會員, 金額
FROM 訂單
GROUP BY 會員

但這樣會報錯。

為什麼?

前面說過,GROUP BY 會員 會把資料壓縮,壓縮後只剩「會員」欄位可以用。

金額欄位在壓縮的過程中已經消失了,所以沒辦法直接 SELECT。

規則:SELECT 的欄位必須出現在 GROUP BY 裡

這是一個很重要的規則:

如果你用了 GROUP BY,那 SELECT 裡面的欄位,必須出現在 GROUP BY 裡面。

為什麼?

因為 GROUP BY 會把資料壓縮,壓縮後只剩下 GROUP BY 指定的欄位還存在。

其他欄位在壓縮過程中消失了,沒辦法直接 SELECT。

用多個欄位分組

老闆說:「那我想看每個會員用過哪些物流方式。」

假設訂單表多了一個「物流方式」欄位:

訂單編號會員金額物流方式
1Alice100宅配
2Alice200超商取貨
3Bob150宅配
4Alice300宅配
5Bob250宅配
會員Alice
金額100
物流方式宅配
會員Alice
金額200
物流方式超商取貨
會員Bob
金額150
物流方式宅配
會員Alice
金額300
物流方式宅配
會員Bob
金額250
物流方式宅配

這時候你需要用「會員 + 物流方式」的組合來分組:

SELECT 會員, 物流方式
FROM 訂單
GROUP BY 會員, 物流方式

GROUP BY 會員, 物流方式 會把「會員和物流方式都相同」的放在一起:

組別訂單編號會員金額物流方式
第一組1Alice100宅配
第一組4Alice300宅配
第二組2Alice200超商取貨
第三組3Bob150宅配
第三組5Bob250宅配
訂單編號1
會員Alice
金額100
物流方式宅配
訂單編號4
會員Alice
金額300
物流方式宅配
訂單編號2
會員Alice
金額200
物流方式超商取貨
訂單編號3
會員Bob
金額150
物流方式宅配
訂單編號5
會員Bob
金額250
物流方式宅配

Alice 宅配有 2 筆,變成一組。

Alice 超商取貨有 1 筆,自己一組。

Bob 宅配有 2 筆,變成一組。

然後把每一組壓縮成一列。

壓縮的時候,資料庫會看 GROUP BY 會員, 物流方式,知道要保留「會員」和「物流方式」這兩個欄位。

其他欄位(訂單編號、金額)在壓縮過程中消失。

結果是:

會員物流方式
Alice宅配
Alice超商取貨
Bob宅配
物流方式宅配
物流方式超商取貨
物流方式宅配

這是 GROUP BY 壓縮後的結果。

最後 SELECT 會員, 物流方式,就是從這個結果中取出這兩個欄位顯示出來:

會員物流方式
Alice宅配
Alice超商取貨
Bob宅配
物流方式宅配
物流方式超商取貨
物流方式宅配

這就是所有「會員 + 物流方式」的不重複組合。

搭配 WHERE 和 ORDER BY

範例:篩選成功訂單並排序

老闆說:「幫我把訂單狀態是『成功』的挑出來,然後按照物流方式排序。」

假設訂單表多了一個「訂單狀態」欄位:

訂單編號會員金額物流方式訂單狀態
1Alice100宅配成功
2Alice200超商取貨成功
3Bob150宅配失敗
4Alice300宅配成功
5Bob250宅配成功
會員Alice
金額100
物流方式宅配
訂單狀態成功
會員Alice
金額200
物流方式超商取貨
訂單狀態成功
會員Bob
金額150
物流方式宅配
訂單狀態失敗
會員Alice
金額300
物流方式宅配
訂單狀態成功
會員Bob
金額250
物流方式宅配
訂單狀態成功

這時候你需要結合 WHERE、GROUP BY、ORDER BY:

SELECT 會員, 物流方式
FROM 訂單
WHERE 訂單狀態 = '成功'
GROUP BY 會員, 物流方式
ORDER BY 物流方式

SQL 的執行順序

這段 SQL 是怎麼執行的?

第一步:FROM 訂單

先找到訂單表,拿到全部 5 筆資料。

第二步:WHERE 訂單狀態 = ‘成功’

篩選出訂單狀態是「成功」的資料,剩下 4 筆:

訂單編號會員金額物流方式訂單狀態
1Alice100宅配成功
2Alice200超商取貨成功
4Alice300宅配成功
5Bob250宅配成功
會員Alice
金額100
物流方式宅配
訂單狀態成功
會員Alice
金額200
物流方式超商取貨
訂單狀態成功
會員Alice
金額300
物流方式宅配
訂單狀態成功
會員Bob
金額250
物流方式宅配
訂單狀態成功

第三步:GROUP BY 會員, 物流方式

把「會員和物流方式都相同」的放在一起,然後壓縮:

會員物流方式
Alice宅配
Alice超商取貨
Bob宅配
物流方式宅配
物流方式超商取貨
物流方式宅配

第四步:SELECT 會員, 物流方式

從壓縮後的結果取出這兩個欄位顯示。

第五步:ORDER BY 物流方式

按照物流方式排序,結果是:

會員物流方式
Alice宅配
Bob宅配
Alice超商取貨
物流方式宅配
物流方式宅配
物流方式超商取貨

GROUP BY 的執行順序

前面的範例展示了 SQL 的執行順序,這裡整理成表格方便查閱:

順序指令說明
1FROM先找到資料來源
2WHERE篩選出符合條件的資料
3GROUP BY把資料分組並壓縮
4SELECT從壓縮後的結果取出欄位
5ORDER BY排序
指令FROM
說明先找到資料來源
指令WHERE
說明篩選出符合條件的資料
指令GROUP BY
說明把資料分組並壓縮
指令SELECT
說明從壓縮後的結果取出欄位
指令ORDER BY
說明排序

為什麼 WHERE 在 GROUP BY 之前?

因為要先篩選資料,再進行分組。

如果先分組再篩選,可能會發現某些已經分好組的資料其實應該被排除,這樣就白做工了。

為什麼 GROUP BY 在 SELECT 之前?

因為要先分組並壓縮,SELECT 才知道有哪些欄位可以用。

如果還沒分組,就無法確定哪些欄位在壓縮後還存在。

GROUP BY 不能用 SELECT 的別名

因為 GROUP BY 在 SELECT 之前執行,所以不能在 GROUP BY 裡使用 SELECT 定義的別名。

-- 這樣寫理論上會報錯
SELECT 會員 AS member
FROM 訂單
GROUP BY member

有些資料庫(例如 PostgreSQL)允許這樣寫,但建議不要這樣做,原因是:

  1. 這違反了執行順序的邏輯
  2. 很多資料庫不支援這種寫法

如果需要在不同的資料庫之間切換,避免使用這種寫法會比較安全。

小結

這篇文章介紹了 GROUP BY 分組:

  1. 分組的概念:按照欄位把資料分成一堆一堆
  2. 找出不重複資料:GROUP BY 會把重複的資料揉成一團
  3. 看不到個別資料:分組後只能看到分組依據和聚合結果
  4. SELECT 欄位的規則:必須出現在 GROUP BY 或被聚合函數包起來
  5. 多欄位分組:可以用多個欄位的組合來分組
  6. 資料會變少:分組後資料列通常會變少
  7. 執行順序:FROM → WHERE → GROUP BY → SELECT → ORDER BY

下一篇會學習如何在分組之後再做篩選,也就是 HAVING 子句。

上一篇SQL SELECT 入門:什麼是聚合運算?
下一篇SQL SELECT 入門:GROUP BY 搭配聚合函數
目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

資料庫

目錄

  • 什麼是分組?
  • 用 GROUP BY 找出不重複的資料
  • GROUP BY 之後,看不到個別資料
  • 規則:SELECT 的欄位必須出現在 GROUP BY 裡
  • 用多個欄位分組
  • 搭配 WHERE 和 ORDER BY
  • 範例:篩選成功訂單並排序
  • SQL 的執行順序
  • GROUP BY 的執行順序
  • 為什麼 WHERE 在 GROUP BY 之前?
  • 為什麼 GROUP BY 在 SELECT 之前?
  • GROUP BY 不能用 SELECT 的別名
  • 小結