相關子查詢 的核心特徵:子查詢參考了外部查詢的欄位,導致子查詢必須針對外部查詢的每一列重新執行一次。
-- 經典子查詢:獨立執行一次,結果供外部使用
SELECT * FROM Orders WHERE CustomerID IN (SELECT CustomerID FROM Customers WHERE City = 'London');
-- 相關子查詢:外部每一列都要執行一次子查詢
SELECT OrderID, OrderDate,
(SELECT COUNT(*) FROM OrderDetails od WHERE od.OrderID = o.OrderID) AS DetailCount
FROM Orders o;
⚠ 寫得好的相關子查詢有時比多重 JOIN + 暫存表更快,關鍵在於邏輯精簡與索引支援。
CREATE TABLE SalesHistory (
SaleID INT IDENTITY(1,1),
Product VARCHAR(30),
SaleDate SMALLDATETIME,
SalePrice MONEY
);
-- 插入 300 筆測試資料(略)
SELECT
sh1.Product,
sh1.SaleDate,
sh1.SalePrice,
(SELECT SUM(sh2.SalePrice)
FROM SalesHistory sh2
WHERE sh2.Product = sh1.Product
AND sh2.SaleDate <= sh1.SaleDate) AS RunningTotal
FROM SalesHistory sh1
ORDER BY sh1.Product, sh1.SaleDate;
邏輯解析:對每一筆銷售記錄,子查詢計算「同產品且日期不晚於該筆」的銷售額總和,形成累計值。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 相關子查詢 | 語意清晰、單一陳述式完成、無需臨時物件 | 大資料集效能風險 | 報表累計、逐列比較、邏輯難以 JOIN 表達 |
| 視窗函數 (SUM() OVER) | 效能極佳、語法標準、單次掃描 | SQL Server 2012+ 才完整支援 | 現代版本首選方案 |
| 暫存表 + 迴圈/游標 | 可控制批次大小、適合極複雜邏輯 | 程式碼冗長、維護困難 | 舊版本、極特殊業務邏輯 |
JOIN 或 APPLY (CROSS/OUTER APPLY)?SELECT * 導致不必要的資料傳輸?SELECT sh1.Product, sh1.SaleDate, sh1.SalePrice, ca.RunningTotal
FROM SalesHistory sh1
CROSS APPLY (
SELECT SUM(sh2.SalePrice) AS RunningTotal
FROM SalesHistory sh2
WHERE sh2.Product = sh1.Product
AND sh2.SaleDate <= sh1.SaleDate
) ca
ORDER BY sh1.Product, sh1.SaleDate;
CROSS APPLY 語意等同相關子查詢,但更易閱讀、優化器處理更佳,推薦用於 SQL Server 2005+ 環境。