在財務、庫存、物流等場景中,依時間序列計算「執行中總計」是核心需求。本文以銀行帳戶交易為例,示範如何在 SQL Server 中高效實現累計餘額查詢,並探討效能優化策略。
假設交易表 Transactions 結構如下:
TxnID INT PRIMARY KEY
TxnDate DATETIME DEFAULT GETDATE()
Amount MONEY -- 正數=存入,負數=提領
範例資料:
| TxnID | TxnDate | Amount |
|---|---|---|
| 1 | 2006-11-03 02:33:42.340 | 10000.00 |
| 2 | 2006-11-03 02:34:50.467 | -500.00 |
| 3 | 2006-11-03 02:35:04.857 | 250.00 |
| 4 | 2006-11-03 02:42:19.763 | -124.25 |
利用自我連接,將每筆交易與「時間早於或等於該筆」的所有交易聚合:
SELECT
T1.TxnID,
T1.TxnDate,
T1.Amount,
SUM(T2.Amount) AS RunningBalance
FROM Transactions T1
JOIN Transactions T2
ON T2.TxnDate <= T1.TxnDate
AND (T2.TxnDate < T1.TxnDate OR T2.TxnID <= T1.TxnID) -- 同秒排序保護
GROUP BY T1.TxnID, T1.TxnDate, T1.Amount
ORDER BY T1.TxnDate, T1.TxnID;
結果:
| TxnID | TxnDate | Amount | RunningBalance |
|---|---|---|---|
| 1 | 2006-11-03 02:33:42.340 | 10000.00 | 10000.00 |
| 2 | 2006-11-03 02:34:50.467 | -500.00 | 9500.00 |
| 3 | 2006-11-03 02:35:04.857 | 250.00 | 9750.00 |
| 4 | 2006-11-03 02:42:19.763 | -124.25 | 9625.75 |
新增 AccountID 欄位,查詢改為:
SELECT
T1.AccountID, T1.TxnID, T1.TxnDate, T1.Amount,
SUM(T2.Amount) AS RunningBalance
FROM Transactions T1
JOIN Transactions T2
ON T2.AccountID = T1.AccountID
AND T2.TxnDate <= T1.TxnDate
AND (T2.TxnDate < T1.TxnDate OR T2.TxnID <= T1.TxnID)
WHERE T1.AccountID = @TargetAccount -- 可選參數
GROUP BY T1.AccountID, T1.TxnID, T1.TxnDate, T1.Amount
ORDER BY T1.AccountID, T1.TxnDate, T1.TxnID;
CREATE INDEX IX_Transactions_Acc_Date_ID ON Transactions(AccountID, TxnDate, TxnID) INCLUDE (Amount),實現索引覆蓋與順序掃描。SUM(Amount) OVER (PARTITION BY AccountID ORDER BY TxnDate, TxnID ROWS UNBOUNDED PRECEDING),執行計畫更優、碼更簡潔。同樣邏輯適用於:
掌握累計值查詢,不僅能滿足報表需求,更能在異常交易排查、審計追蹤中快速定位問題節點。