WHERE 與 HAVING 都是設定查詢條件,但作用階層、支援函數、適用場景截然不同。本文從查詢執行流程、語法限制、實戰案例三個維度,徹底釐清兩者差異,助您寫出正確且高效的分組查詢。
| 維度 | WHERE | HAVING |
|---|---|---|
| 作用對象 | 單筆資料列 | 分組後的群組 |
| 執行順序 | GROUP BY 之前 | GROUP BY 之後 |
| 可用聚合函數 | 不可 | 可(SUM、COUNT 等) |
| 典型用途 | 資料清洗、篩選基礎列 | 分組品質控管、彙總條件 |
1. FROM / JOIN → 產生基礎資料集
2. WHERE → 列級過濾(移除不符合條件的列)
3. GROUP BY → 依指定欄位分組
4. 聚合函數 → 計算每組 SUM/COUNT/AVG...
5. HAVING → 組級過濾(移除不符合彙總條件的組)
6. SELECT → 投影輸出欄位
7. ORDER BY → 排序最終結果
需求:統計每位主管名下員工數,但只統計受僱日期在 1993 年後的員工。
SELECT ReportsTo AS ManagerID, COUNT(*) AS EmpCount
FROM Employees
WHERE HireDate > '1993-01-01' -- 列級條件,在分組前過濾
GROUP BY ReportsTo;
需求:找出名下員工超過 4 人的主管。
SELECT ReportsTo AS ManagerID, COUNT(*) AS EmpCount
FROM Employees
GROUP BY ReportsTo
HAVING COUNT(*) > 4; -- 組級條件,針對聚合結果過濾
需求:統計 1993 年後受僱員工的主管分布,且只顯示名下≥3人的主管。
SELECT ReportsTo AS ManagerID, COUNT(*) AS EmpCount
FROM Employees
WHERE HireDate > '1993-01-01' -- 先縮減資料集
GROUP BY ReportsTo
HAVING COUNT(*) >= 3; -- 再篩選分組結果
| 錯誤寫法 | 錯誤原因 | 正確寫法 |
|---|---|---|
HAVING HireDate > '1993-01-01' | HireDate 非聚合欄位,不可用於 HAVING | 移至 WHERE |
WHERE COUNT(*) > 4 | 聚合函數不可用於 WHERE | 改用 HAVING COUNT(*) > 4 |
GROUP BY ManagerName HAVING ManagerName = 'John' | 分組鍵值過濾應用 WHERE | WHERE ManagerName = 'John' GROUP BY ManagerName |
SUM(col):加總COUNT(col):非 NULL 筆數;COUNT(*) 含 NULLAVG(col):平均MAX/MIN(col):極值GROUP BY region 使聚合函數按 region 分組計算,每組僅回傳一列需求:找出總銷售額超過全公司平均銷售額的業務員。
SELECT SalesPersonID, SUM(TotalDue) AS TotalSales
FROM SalesOrderHeader
GROUP BY SalesPersonID
HAVING SUM(TotalDue) > (SELECT AVG(TotalSales) FROM
(SELECT SalesPersonID, SUM(TotalDue) AS TotalSales
FROM SalesOrderHeader GROUP BY SalesPersonID) t);
HAVING 使用別名(HAVING EmpCount > 4),但建議寫完整聚合以相容遷移HAVING 不可用 SELECT 別名WHERE 過濾列,HAVING 過濾組;
聚合函數不能進 WHERE,分組之後才能用 HAVING。
掌握執行順序與作用層級,即可在複雜報表查詢中精準選擇 WHERE 或 HAVING,避免語法錯誤與邏輯漏洞,撰寫出既正確又高效的 SQL。