表 ST_StockRecord 含字段:BillDate(日期)、MaterialNo(料號)、SHID(倉庫)、StockQty(出入數量)。需按**月、料號、倉庫**分組,取每組**最後一筆**(按日期倒序)的記錄,輸出日期、料號、倉庫、數量。
SELECT BillDate AS 日期,
MaterialNo AS 料號,
SHID AS 倉庫,
StockQty AS 出入數量
FROM (
SELECT BillDate, MaterialNo, SHID, StockQty,
ROW_NUMBER() OVER (
PARTITION BY YEAR(BillDate), MONTH(BillDate), MaterialNo, SHID
ORDER BY BillDate DESC
) AS rn
FROM ST_StockRecord
) t
WHERE rn = 1
ORDER BY BillDate, MaterialNo, SHID;
rn=1 即各組最新記錄。若存在完全相同 BillDate 的多筆,ROW_NUMBER 會任意取一筆。若需確定性(如取 ID 最大),改用:
ROW_NUMBER() OVER (
PARTITION BY YEAR(BillDate), MONTH(BillDate), MaterialNo, SHID
ORDER BY BillDate DESC, ID DESC -- 假設有自增 ID
) AS rn
| 方法 | 語法 | 優點 | 缺點 |
|---|---|---|---|
| ROW_NUMBER | 如上 | 標準、通用、支援複雜排序 | 需子查詢/CTE |
| TOP 1 WITH TIES | SELECT TOP 1 WITH TIES ... ORDER BY ROW_NUMBER() OVER (...) | 無子查詢 | 較生僻,並列情況返回多行 |
| CROSS APPLY | SELECT ... FROM (SELECT DISTINCT 年,月,料號,倉庫 FROM ST_StockRecord) d CROSS APPLY (SELECT TOP 1 * FROM ST_StockRecord s WHERE ... ORDER BY BillDate DESC) ca | 直觀、索引友好 | 寫法較長 |
| LAST_VALUE | LAST_VALUE(StockQty) OVER (PARTITION BY ... ORDER BY BillDate ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) | 單掃描 | 需配合 DISTINCT 去重,語法複雜 |
CREATE INDEX IX_StockRecord_Query
ON ST_StockRecord (MaterialNo, SHID, BillDate DESC)
INCLUDE (StockQty);
覆蓋索引可避免回表,大幅提升分組排序效能。
開窗函數是解決「分組取 Top-N」問題的瑞士軍刀,掌握 PARTITION BY + ORDER BY + ROW_NUMBER 模式,應對各種「每組最新/最大/最小」需求遊刃有餘。