萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

SQL WHERE 與 HAVING 核心差異深度解析:執行順序決定用法

一句話核心差異

WHERE 在分組前過濾行 (不可用聚合函數);HAVING 在分組後過濾組 (可用聚合函數)。

執行順序視覺化

FROM t1 JOIN t2 ...
    ↓
WHERE 條件過濾行 (WHERE 可用索引)
    ↓
GROUP BY 分組
    ↓
聚合函數計算 (COUNT, SUM, AVG...)
    ↓
HAVING 條件過濾組 (可用聚合函數)
    ↓
SELECT 投影
    ↓
ORDER BY 排序
    ↓
LIMIT 限制

對比範例:Northwind 資料庫

場景一:WHERE 過濾明細行

-- 找訂單金額 > 1000 的訂單明細
SELECT OrderID, SUM(UnitPrice * Quantity) AS Total
FROM OrderDetails
WHERE UnitPrice > 50  -- 明細行級過濾
GROUP BY OrderID;

場景二:HAVING 過濾分組結果

-- 找總金額 > 10000 的訂單
SELECT OrderID, SUM(UnitPrice * Quantity) AS Total
FROM OrderDetails
GROUP BY OrderID
HAVING SUM(UnitPrice * Quantity) > 10000;  -- 組級過濾,需聚合函數

場景三:兩者結合

-- 1997 年、總金額 > 5000 的訂單
SELECT o.OrderID, SUM(od.UnitPrice * od.Quantity) AS Total
FROM Orders o
JOIN OrderDetails od ON o.OrderID = od.OrderID
WHERE o.OrderDate >= '1997-01-01' AND o.OrderDate < '1998-01-01'  -- 行過濾
GROUP BY o.OrderID
HAVING SUM(od.UnitPrice * od.Quantity) > 5000;  -- 組過濾

常見錯誤與修正

錯誤寫法錯誤原因正確寫法
HAVING price > 100price 非聚合欄位,非 GROUP BY 列WHERE price > 100 (行過濾) 或 HAVING MAX(price) > 100 (組過濾)
WHERE COUNT(*) > 5WHERE 階段尚未聚合HAVING COUNT(*) > 5
WHERE col NOT IN (subquery 含 NULL)NOT IN 遇 NULL 返回 UNKNOWNWHERE NOT EXISTS (subquery) 或 LEFT JOIN ... IS NULL

效能啟示

  • WHERE 能用索引:盡早過濾,減少參與分組/排序的行數
  • HAVING 無索引:在結果集上掃描,儘量將條件下推到 WHERE
  • 索引覆蓋:WHERE + GROUP BY 欄位建立複合索引可避免回表

記憶口訣

WHERE 管「行」,行行有索引;
HAVING 管「組」,組組看聚合。
先 WHERE 後 HAVING,順序不能亂。
copyright © 萬盛學電腦網 all rights reserved