5 個讓你看起來像 SQL 新手的 GROUP BY 錯誤(以及如何修正)

更新 發佈閱讀 17 分鐘

如果你曾經盯著 SQL 錯誤訊息,納悶為什麼你那看似完美合理的 GROUP BY 查詢就是跑不動,你絕對不孤單。我記得我第一次寫 GROUP BY 查詢時,花了整整三個小時除錯,當下真心覺得是資料庫壞了。劇透:其實是我自己的問題。

GROUP BY 是 SQL 裡最強大的資料分析功能之一,但同時也是新手最容易踩雷的地方。這些錯誤在有經驗的開發者眼中一目了然,而且會讓你的程式碼看起來很業餘。最令人沮喪的是,這些 SQL GROUP BY 錯誤其實都源自於對幾個核心概念的不理解。

讀完這篇文章,你會明白 5 個最常見的 GROUP BY 錯誤、它們為什麼會發生,以及如何精準修正。更重要的是,你會理解 GROUP BY 背後的邏輯,從此每次寫查詢都能充滿自信。


錯誤 #1:選取了沒有被分組的欄位

這是讓 SQL 新手跌倒的頭號錯誤。你寫了一個看起來很合理的查詢,結果卻得到一個莫名其妙的錯誤訊息。

錯誤長這樣:

-- 錯誤寫法

SELECT 
department,    
first_name,        -- 問題就在這裡!
  COUNT(*) AS emp_count
FROM employees
GROUP BY department;

為什麼會犯這個錯? 當你在思考資料時,這個邏輯看起來很直覺:「給我看部門、員工姓名,還有每個部門有幾個人。」你的大腦會把這三個視為你想看到的獨立資訊。

但 SQL 看到的是:你要求它把多筆資料合併成每個部門一筆。業務部可能有 10 個員工——John、Sarah、Mike 等等。它該顯示哪個 first_name?SQL 不可能隨機挑一個。

你必須記住的核心規則:

你 SELECT 語句中的每個欄位,必須符合以下兩種情況之一:

  1. 出現在 GROUP BY 子句中,或是
  2. 包在聚合函數裡面(COUNT、SUM、AVG、MIN、MAX)

正確的寫法:

-- 正確寫法 - 只選取分組或聚合的欄位

SELECT 
  department,    
COUNT(*) AS emp_count
FROM employees
GROUP BY department;

如果你真的需要個別姓名和統計數字一起出現,那你可能根本不需要 GROUP BY——你需要的是視窗函數(window function,這是另一個主題了)或是用分組子查詢做 JOIN。

什麼時候會遇到這個問題: 最常見的情況是當你想在摘要統計旁邊顯示明細資料時。記住:GROUP BY 會把資料合併。如果你要明細,就別合併。


錯誤 #2:用 WHERE 而不是 HAVING

這個錯誤超級常見,因為 WHERE 通常是你最早學會的篩選關鍵字。每次需要篩選時,直覺就是用 WHERE。

錯誤寫法:

-- 錯誤寫法

SELECT 
  department, 
COUNT(*) AS emp_count
FROM employees
WHERE COUNT(*) > 5        -- 錯了!
GROUP BY department;

為什麼會犯這個錯? WHERE 是你的首選篩選工具,所以當你想要「超過 5 個員工的部門」時,本能就是用 WHERE。

但關鍵的差異在這裡:

  • WHERE 在分組之前篩選個別資料列
  • HAVING 在聚合之後篩選群組

這樣想:你沒辦法在計數之前就用 COUNT(*) 篩選。SQL 需要先依部門分組所有員工、計算數量,然後才能篩選掉 5 人以下的部門。

正確的寫法:

-- 正確寫法 - 對聚合條件使用 HAVING

SELECT 
  department,    
COUNT(*) AS emp_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;

你可以在同一個查詢中同時使用兩者:

SELECT 
  department,
AVG(salary) AS avg_salary
FROM employees
WHERE hire_date >= '2020-01-01'  -- 先篩選個別資料列
GROUP BY department
HAVING AVG(salary) > 60000;      -- 再篩選群組

在這個例子中,WHERE 先把 2020 年以前入職的員工濾掉。然後 GROUP BY 把剩下的員工分組。接著 HAVING 再把平均薪資不到 60,000 的部門濾掉。

記憶技巧: WHERE =「哪些資料列?」| HAVING =「每個群組有多少?」


錯誤 #3:忘記 COUNT(*) 和 COUNT(column) 是不同的

老實說,我用 COUNT(*) 和 COUNT(column_name) 互換著用了好幾個月,才發現它們會回傳不同的結果。

令人困惑的時刻:

-- 你預期會有 100 個客戶

SELECT COUNT(email) AS total_customers
FROM customers;

-- 結果回傳 87... 等等,另外 13 個客戶跑去哪了?

為什麼會犯這個錯? COUNT() 感覺應該永遠都是計算全部。COUNT(*) 和 COUNT(column) 之間的細微差異看起來像是不必要的複雜——直到它咬你一口。

關鍵的差異:

  • COUNT(*) 計算所有資料列(包含某些欄位是 NULL 的資料列)
  • COUNT(column) 只計算該欄位中非 NULL 的值
  • COUNT(DISTINCT column) 計算不重複的非 NULL 值

實際範例:

-- 你的 customers 資料表裡的資料:

-- customer_id | email
-- 1           | john@email.com
-- 2           | NULL
-- 3           | jane@email.com

SELECT 
  COUNT(*) AS total_rows,                -- 回傳:3
  COUNT(email) AS emails_provided,       -- 回傳:2
  COUNT(DISTINCT email) AS unique_emails -- 回傳:2
FROM customers;

客戶 #2 沒有提供電子郵件。COUNT(*) 包含他們,因為這筆資料存在。COUNT(email) 跳過他們,因為 email 欄位是 NULL。

什麼時候這很重要:

  • 資料品質分析:「有多少客戶提供了電話號碼?」用 COUNT(phone_number)。
  • 避免重複計算:「賣出了幾種不同的產品?」用 COUNT(DISTINCT product_id)。
  • 精確度:「總客戶數」(用 COUNT(*))vs「有電子郵件的客戶數」(用 COUNT(email))。

解決方法: 刻意選擇你需要的 COUNT。問自己:「我在計算的東西,NULL 值重要嗎?」


錯誤 #4:沒有處理聚合函數中的 NULL

這個錯誤很陰險,因為你的查詢會成功執行——只是給你錯誤的答案。

看不見的錯誤:

SELECT AVG(discount) AS avg_discount
FROM orders;

-- 回傳 15.5,但你預期是 10.8
-- 發生什麼事了?

為什麼會犯這個錯? 你假設 NULL 值會被當作 0 來計算。錯了,它們會被完全忽略。

令人意外的行為:

-- 訂單資料範例:

-- order_id | discount
-- 1        | 10
-- 2        | 20
-- 3        | NULL
-- 4        | 15

SELECT AVG(discount) FROM orders;

-- 回傳:15(計算方式是 (10+20+15)/3
-- 不是:11.25(這樣算才對:(10+20+0+15)/4

NULL 值會從加總和計數中都被排除。所以如果 NULL 代表「沒有折扣」(本質上是 0),你的平均值就錯了。你計算的是「有給折扣時的平均折扣」而不是「每筆訂單的平均折扣」。

修正方法——選項 1(把 NULL 當作 0):

SELECT AVG(COALESCE(discount, 0)) AS avg_discount
FROM orders;

-- COALESCE 回傳第一個非 NULL 的值
-- 現在 NULL 在平均前會被替換成 0

修正方法——選項 2(兩種觀點都顯示):

SELECT 
  AVG(discount) AS avg_discount_when_given,
  AVG(COALESCE(discount, 0)) AS avg_discount_per_order
FROM orders;

所有聚合函數都會忽略 NULL:

  • SUM(column) - NULL 被排除在總和之外
  • MIN(column)、MAX(column) - 忽略 NULL
  • COUNT(column) - 不計算 NULL

唯一的例外是 COUNT(*),它會計算所有資料列,不管是否有 NULL 值。

什麼時候會遇到這個問題: 財務計算、選填欄位、有遺漏值的資料、轉換率計算。


錯誤 #5:在 GROUP BY 中重複複雜的表達式

這個錯誤不會造成錯誤——只是讓你的程式碼又醜又難讀,而且容易打錯字。

尷尬的查詢:

-- 能跑,但維護起來很痛苦

SELECT 
CASE 
   WHEN age < 30 THEN '30 歲以下'
    WHEN age BETWEEN 30 AND 50 THEN '30-50 歲'
    ELSE '50 歲以上'
  END AS age_group,
  COUNT(*) AS customer_count
FROM customers
GROUP BY 
CASE 
  WHEN age < 30 THEN '30 歲以下'
WHEN age BETWEEN 30 AND 50 THEN '30-50 歲'
    ELSE '50 歲以上'
  END;

為什麼會犯這個錯? 你期待欄位別名在 GROUP BY 中可以用,就像在 ORDER BY 中一樣。不幸的是,在標準 SQL 中不行。

現實是: 你必須重複整個表達式,因為 GROUP BY 在 SELECT 之前執行(別名是在 SELECT 中建立的)。SQL 還不知道 age_group 是什麼。

更好的做法——使用子查詢:

SELECT 
  age_group,
COUNT(*) AS customer_count
FROM (
  SELECT 
CASE 
     WHEN age < 30 THEN '30 歲以下'
      WHEN age BETWEEN 30 AND 50 THEN '30-50 歲'
      ELSE '50 歲以上'
    END AS age_group
  FROM customers
) AS categorized
GROUP BY age_group;

現在表達式在子查詢中只寫一次,外層查詢使用乾淨的別名。

注意: PostgreSQL 和其他某些資料庫允許 GROUP BY 1(位置)或 GROUP BY age_group(別名),但這不是標準 SQL。如果你寫的查詢可能在不同資料庫上執行,就用子查詢的方法。


加碼:為什麼會犯這些錯誤(執行順序的秘密)

大多數 GROUP BY 錯誤都源自一個誤解:SQL 不是按照你寫的順序執行查詢的。

你寫的順序:

SELECT ...
FROM ...
WHERE ...
GROUP BY ... 
HAVING ... 
ORDER BY ... 
LIMIT ...

SQL 執行的順序:

1. FROM       — 從資料表取得資料
2. WHERE      — 篩選個別資料列
3. GROUP BY   — 分組剩餘的資料列
4. HAVING     — 篩選群組
5. SELECT     — 選取欄位並計算
6. ORDER BY   — 排序結果
7. LIMIT      — 限制輸出

這個執行順序解釋了一切:

  • 為什麼 WHERE 不能用聚合函數: 在步驟 2 時聚合還不存在。
  • 為什麼你不能在 GROUP BY 中使用 SELECT 的別名: SELECT 在 GROUP BY 之後才執行。
  • 為什麼 HAVING 可以篩選聚合: 聚合是在步驟 3 的 GROUP BY 期間計算的。

一旦你內化了這個順序,GROUP BY 查詢就會變得直覺。

記憶口訣:了再好來、最後制」(From、Where、Group、Having、Select、Order、Limit)


結語

讓你看起來像 SQL 新手的五個錯誤:

  1. 選取沒有分組的欄位卻沒有聚合它們
  2. 使用 WHERE 來篩選聚合,而不是用 HAVING
  3. 混淆 COUNT(*) 和 COUNT(column)
  4. 忽略聚合函數中的 NULL 行為
  5. 重複複雜表達式而不是使用子查詢

把所有東西串起來的關鍵洞察是:大多數 GROUP BY 錯誤都來自對 SQL 執行順序的不理解。一旦你掌握了 WHERE 在分組前篩選、HAVING 在分組後篩選——而且 SELECT 是最後才執行——所有東西就會豁然開朗。

你的行動步驟: 下次寫 GROUP BY 查詢時,執行前先暫停,問自己:「我 SELECT 中的每個欄位,是不是都在我的 GROUP BY 裡,或是包在聚合函數裡?」這一個問題就能避免 80% 的 GROUP BY 錯誤。

記住:這些錯誤完全正常。每個 SQL 開發者都犯過。新手和專業人士的差別不在於避免錯誤——而在於能在 30 秒內辨識並修正,而不是花 30 分鐘。


實戰挑戰

在離開之前,試著修正這個包含多個錯誤的查詢:

-- 你能找出幾個錯誤?

SELECT 
department,
first_name,
COUNT(*) AS emp_count,
  AVG(bonus) AS avg_bonus
FROM employees
WHERE COUNT(*) > 3
GROUP BY department;

答案: 三個錯誤!

  1. first_name 沒有在 GROUP BY 中,也沒有在聚合函數裡
  2. WHERE 使用了聚合函數(應該用 HAVING)
  3. AVG(bonus) 會忽略 NULL——如果有些員工的獎金是 NULL,要決定這是不是你要的

修正後的查詢:

SELECT 
department,
COUNT(*) AS emp_count,
  AVG(COALESCE(bonus, 0)) AS avg_bonus
FROM employees
GROUP BY department
HAVING COUNT(*) > 3;

現在你寫 GROUP BY 查詢就像個專家了。放膽去聚合吧,帶著自信!



留言
avatar-img
Leon Wong 282
4會員
23內容數
Hi,我是 Leon Wong(亮之)——電腦科學與開發愛好者,也是 Notion 重度使用者。如果你想更高效地學習與創作,這裡會是你的實用資源。
Leon Wong 282的其他內容
2025/10/26
你知道 SQL 的 COUNT() 其實有三種用法嗎?COUNT(*)、COUNT(欄位) 和 COUNT(DISTINCT 欄位) 會給出完全不同的結果。本文用實際範例教你在每種情況下該用哪一種,避免資料分析出錯。適合 SQL 初學者和想打好基礎的開發者閱讀。
Thumbnail
2025/10/26
你知道 SQL 的 COUNT() 其實有三種用法嗎?COUNT(*)、COUNT(欄位) 和 COUNT(DISTINCT 欄位) 會給出完全不同的結果。本文用實際範例教你在每種情況下該用哪一種,避免資料分析出錯。適合 SQL 初學者和想打好基礎的開發者閱讀。
Thumbnail
2025/10/25
剛裝好 VS Code 卻不知道怎麼設定?這 7 個內建設定只要 5 分鐘,就能讓你避免程式碼遺失、排版混亂、眼睛疲勞等新手常見問題。不需要任何擴充套件,立即提升寫程式體驗。
Thumbnail
2025/10/25
剛裝好 VS Code 卻不知道怎麼設定?這 7 個內建設定只要 5 分鐘,就能讓你避免程式碼遺失、排版混亂、眼睛疲勞等新手常見問題。不需要任何擴充套件,立即提升寫程式體驗。
Thumbnail
2025/10/24
手動下載軟體浪費時間又有資安風險。這篇文章教你用 10 分鐘設定 Homebrew 套件管理工具,從此一行指令就能安裝、更新所有 Mac 軟體。適合完全新手,只需要會複製貼上。
Thumbnail
2025/10/24
手動下載軟體浪費時間又有資安風險。這篇文章教你用 10 分鐘設定 Homebrew 套件管理工具,從此一行指令就能安裝、更新所有 Mac 軟體。適合完全新手,只需要會複製貼上。
Thumbnail
看更多
你可能也想看
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
※ 何時該使用 JOIN? JOIN 使用的時機是:當你需要同時查詢一張以上的資料表的時候。 ※ SQL有哪些TABLE JOIN的方式? INNER JOIN LEFT JOIN RIGHT JOIN SELF JOIN ※ 使用 JOIN 的時候,我們需要考慮到: 我要使用哪一種
Thumbnail
※ 何時該使用 JOIN? JOIN 使用的時機是:當你需要同時查詢一張以上的資料表的時候。 ※ SQL有哪些TABLE JOIN的方式? INNER JOIN LEFT JOIN RIGHT JOIN SELF JOIN ※ 使用 JOIN 的時候,我們需要考慮到: 我要使用哪一種
Thumbnail
※ 關聯式資料庫(RDBMS)是什麼? 關聯式資料庫(RDBMS)是一種傳統的資料庫系統,以結構化查詢語言(SQL)為基礎,將資料儲存於預定義的表格中。這些表格包括行和列,彼此之間存在明確的關聯性。 ※ 關聯式資料庫(RDBMS)有兩個重要元素: 關聯(Relational): 關聯式資料庫
Thumbnail
※ 關聯式資料庫(RDBMS)是什麼? 關聯式資料庫(RDBMS)是一種傳統的資料庫系統,以結構化查詢語言(SQL)為基礎,將資料儲存於預定義的表格中。這些表格包括行和列,彼此之間存在明確的關聯性。 ※ 關聯式資料庫(RDBMS)有兩個重要元素: 關聯(Relational): 關聯式資料庫
Thumbnail
題目敘述 題目會給我們一張Views資料表。裡面分別有article_id、author_id、viewer_id、view_date等欄位。題目說這張資料表沒有主鍵Primary key,而且可能有重複欄位。 題目要求我們列出所有讀過自己寫的文章的作者ID 輸出答案時,請以作者ID做升序排列
Thumbnail
題目敘述 題目會給我們一張Views資料表。裡面分別有article_id、author_id、viewer_id、view_date等欄位。題目說這張資料表沒有主鍵Primary key,而且可能有重複欄位。 題目要求我們列出所有讀過自己寫的文章的作者ID 輸出答案時,請以作者ID做升序排列
Thumbnail
聚合函數 可以對資料的筆數、平均、最大、最小和加總的運算,提供查詢結果:如下表示: COUNT(Column):計算筆數,「*」是統計紀錄數。 AVG(Column):計算欄位平均值。 MAX(Column):計算欄位最大值。 MIN(Column):計算欄位最小值。 SUM(Colum
Thumbnail
聚合函數 可以對資料的筆數、平均、最大、最小和加總的運算,提供查詢結果:如下表示: COUNT(Column):計算筆數,「*」是統計紀錄數。 AVG(Column):計算欄位平均值。 MAX(Column):計算欄位最大值。 MIN(Column):計算欄位最小值。 SUM(Colum
Thumbnail
多條件查詢 AND運算子 SELECT *​ FROM your_table_name WHERE column1 LIKE '_value1%' AND column2 = number​2 OR運算子 SELECT *​ FROM your_table_name WHERE colu
Thumbnail
多條件查詢 AND運算子 SELECT *​ FROM your_table_name WHERE column1 LIKE '_value1%' AND column2 = number​2 OR運算子 SELECT *​ FROM your_table_name WHERE colu
Thumbnail
查詢範圍 指定欄位 SELECT column1, column2, column3,... FROM your_table_name 不重複欄位 SELECT DISTINCT column1 FROM your_table_name 欄位別名 SELECT column1 A
Thumbnail
查詢範圍 指定欄位 SELECT column1, column2, column3,... FROM your_table_name 不重複欄位 SELECT DISTINCT column1 FROM your_table_name 欄位別名 SELECT column1 A
Thumbnail
如果你 WHERE 的條件需要常常更動,但每次都要進去 QUERY 所在的儲存格編輯語法,實在是有點太麻煩了?但其實有個小技巧,可以讓 QUERY 更新得更輕鬆。來看看吧!
Thumbnail
如果你 WHERE 的條件需要常常更動,但每次都要進去 QUERY 所在的儲存格編輯語法,實在是有點太麻煩了?但其實有個小技巧,可以讓 QUERY 更新得更輕鬆。來看看吧!
Thumbnail
你知道 SELECT 除了回傳欄位、使用聚集函數外,還可以更進一步做加減乘除喔!這個系列是 QUERY 函式大解析的第六篇文章,如果還不知道什麼是 QUERY 的話,我還是很建議你從第一篇慢慢看、跟著我們的練習實際操作,就會更有概念囉~
Thumbnail
你知道 SELECT 除了回傳欄位、使用聚集函數外,還可以更進一步做加減乘除喔!這個系列是 QUERY 函式大解析的第六篇文章,如果還不知道什麼是 QUERY 的話,我還是很建議你從第一篇慢慢看、跟著我們的練習實際操作,就會更有概念囉~
Thumbnail
今天要介紹的是進階的 SELECT 功能,可以即時對 QUERY 的結果運算,迅速取得數值的平均、總和、最大值、最小值和數量,省去拉資料透視表(pivot table)的麻煩!
Thumbnail
今天要介紹的是進階的 SELECT 功能,可以即時對 QUERY 的結果運算,迅速取得數值的平均、總和、最大值、最小值和數量,省去拉資料透視表(pivot table)的麻煩!
Thumbnail
select sql遇到類似這個問題: SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of ORDER BY clause is not in GROUP BY clause and contains no
Thumbnail
select sql遇到類似這個問題: SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of ORDER BY clause is not in GROUP BY clause and contains no
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News