首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

T-SQL 進階查詢技巧:從相關子查詢到條件聚合的效能優化實戰

     編輯:關於SqlServer

前言

T-SQL 解決同一問題往往有多種寫法,效能卻可能天差地別。本文以經典 Northwind 資料庫 [Order Details] 資料表為例,逐步演示四種查詢「不含產品 59 的訂單總金額」的寫法,並透過執行計畫對比,揭示條件聚合(Conditional Aggregation)如何以單次掃描擊敗多次掃描的相關子查詢。

情境設定

  • [Order Details]:訂單明細表,含 OrderID、ProductID、UnitPrice、Quantity。
  • 目標:統計完全不包含 ProductID = 59的訂單總金額(若訂單含 59 號產品,整張訂單排除)。

四種寫法與效能對比

寫法 1:簡單過濾(錯誤示範)

SELECT OrderID, SUM(Quantity * UnitPrice) AS TotalValue
FROM [Order Details]
WHERE ProductID <> 59
GROUP BY OrderID;

缺陷:僅過濾掉 59 號產品的明細行,同一訂單若含其他產品仍會被統計,不符合業務需求。雖效能最佳(單次掃描),但結果錯誤。

寫法 2:NOT EXISTS 相關子查詢

SELECT o1.OrderID, SUM(o1.Quantity * o1.UnitPrice) AS TotalValue
FROM [Order Details] o1
WHERE NOT EXISTS (
    SELECT 1 FROM [Order Details] o2
    WHERE o2.OrderID = o1.OrderID AND o2.ProductID = 59
)
GROUP BY o1.OrderID;

機制:外層每行觸發內層查詢,檢查該訂單是否存在 59 號產品。執行計畫顯示兩次資料表掃描(外層 + 內層),資料量大時效能急劇下降。

寫法 3:NOT IN 子查詢

SELECT o1.OrderID, SUM(o1.Quantity * o1.UnitPrice) AS TotalValue
FROM [Order Details] o1
WHERE 59 NOT IN (
    SELECT ProductID FROM [Order Details] o2 WHERE o2.OrderID = o1.OrderID
)
GROUP BY o1.OrderID;

本質同寫法 2,同樣產生相關子查詢與雙重掃描。且 NOT IN 若子查詢回傳 NULL 會導致整體判斷為 UNKNOWN,隱藏邏輯陷阱。

寫法 4:條件聚合 + HAVING(最佳解)

SELECT OrderID, SUM(Quantity * UnitPrice) AS TotalValue
FROM [Order Details]
GROUP BY OrderID
HAVING SUM(CASE WHEN ProductID = 59 THEN 1 ELSE 0 END) = 0;

精妙之處:

  • CASE WHEN ProductID = 59 THEN 1 ELSE 0 END 標記含 59 號產品的行。
  • SUM(...) = 0 代表該群組完全沒有 59 號產品。
  • 單次資料表掃描,無相關子查詢,執行計畫極其精簡,效能碾壓前兩種寫法。

延伸範例:找出「僅透過單一業務員下單」的客戶

SELECT DISTINCT o1.CustomerID
FROM Orders o1
WHERE NOT EXISTS (
    SELECT 1 FROM Orders o2
    WHERE o2.CustomerID = o1.CustomerID
      AND o2.EmployeeID <> o1.EmployeeID
);

同樣可改寫為條件聚合:

SELECT CustomerID
FROM Orders
GROUP BY CustomerID
HAVING MIN(EmployeeID) = MAX(EmployeeID);

(假設 EmployeeID 不為 NULL;若有 NULL 需額外處理)

核心心法總結

模式適用場景效能特徵建議
相關子查詢 (NOT EXISTS/IN)邏輯直觀、資料量小、缺乏索引支援N+1 問題、多次掃描資料量 > 萬筆時避免
條件聚合 (CASE + HAVING)集合式過濾、需檢查群組內條件單次掃描、極佳擴展性首選模式
JOIN + 派生表/CTE複雜多表關聯、需重用中間結果視連接策略而定配合索引使用

掌握「用集合思維取代逐行思維」,是 T-SQL 進階的關鍵。下次寫 WHERE NOT EXISTS 時,先問自己:能否用 HAVING + CASE 解決?

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved