SQL Server 2012 引入 ANSI SQL:2003 標準窗口函數,FIRST_VALUE、LAST_VALUE、LEAD、LAG 讓集合運算能輕鬆存取「第一筆/最後一筆/前後 N 筆」資料,徹底改寫以往需自聯結/子查詢/游標才能完成的邏輯。本文深度解析 FIRST_VALUE 語法、行為細節與實戰場景。
FIRST_VALUE (scalar_expression) OVER (
[ PARTITION BY partition_expression [ , ... ] ]
ORDER BY order_expression [ ASC | DESC ] [ , ... ]
[ ROWS | RANGE BETWEEN frame_start AND frame_end ]
)
scalar_expression:要取回的純量運算式 (欄位、函數、常數)。PARTITION BY:分區,函數在各分區內獨立計算。ORDER BY:決定「第一筆」的排序依據,**必填**。ROWS/RANGE:框架子句,預設 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,對 FIRST_VALUE 通常不影響結果,但建議顯式指定 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 取分區絕對第一筆。DECLARE @TestData TABLE (
ID INT IDENTITY(1,1),
Department VARCHAR(20),
LastName VARCHAR(20),
Rate FLOAT
);
INSERT INTO @TestData (Department, LastName, Rate) VALUES
('Document Control','Arifin',17.7885),
('Document Control','Norred',16.8269),
('Document Control','Kharatishvili',16.8269),
('Information Services','Chai',10.25),
('Information Services','Berge',10.25),
('Information Services','Trenary',50.4808),
('Information Services','Conroy',39.6635),
('Information Services','Ajenstat',38.4615),
('Information Services','Wilson',38.4615),
('Information Services','Connelly',32.4519),
('Information Services','Meyyappan',32.4519);
SELECT *,
FIRST_VALUE(LastName) OVER (ORDER BY ID ASC) AS FirstByIdAsc,
FIRST_VALUE(LastName) OVER (ORDER BY ID DESC) AS FirstByIdDesc
FROM @TestData;
結果:FirstByIdAsc 全部顯示 Arifin (ID=1);FirstByIdDesc 全部顯示 Meyyappan (ID=11)。
SELECT *,
FIRST_VALUE(LastName) OVER (PARTITION BY Department ORDER BY ID ASC) AS FirstInDept
FROM @TestData;
結果:Document Control 群組顯示 Arifin;Information Services 群組顯示 Chai。
SELECT *,
FIRST_VALUE(LastName) OVER (PARTITION BY Department ORDER BY Rate DESC) AS TopEarner
FROM @TestData;
結果:各部門薪資最高者 (Trenary / Kharatishvili) 顯示於每行,便於同列比較。
| 場景 | SQL 片段 | 說明 |
|---|---|---|
| 各群組基準值對比 | SalesAmt - FIRST_VALUE(SalesAmt) OVER (PARTITION BY Region ORDER BY SaleDate) AS DiffFromFirst | 每日銷售額與該區域首日差異 |
| 客戶首單資訊攜帶 | FIRST_VALUE(OrderDate) OVER (PARTITION BY CustID ORDER BY OrderDate) AS FirstOrderDate | 每筆訂單都能看到該客戶首單日期 |
| 異常偵測基準線 | CASE WHEN Metric > 1.5 * FIRST_VALUE(Metric) OVER (PARTITION BY Sensor ORDER BY Ts) THEN 1 ELSE 0 END | 以首次讀數為基準偵測漂移 |
PARTITION BY + ORDER BY 欄位建立覆蓋索引,避免 Sort 運算子。ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,語意清晰且避免 RANGE 對重複值的預設框架歧義。CROSS APPLY (SELECT TOP 1 ... ORDER BY ...) 或 ROW_NUMBER()=1 子查詢,FIRST_VALUE 更簡潔、優化器更易優化。FIRST_VALUE 以聲明式語法解決「取分區第一筆」經典問題,配合 PARTITION BY、ORDER BY、框架子句,靈活應對基準對比、首單屬性攜帶、異常偵測等場景。升級 SQL Server 2012+ 後,請大膽替換舊式自聯結/子查詢寫法,享受更簡潔、更高效的查詢表達。