---
disqus: kevinwang0510
---
# [SQL 咨詢室系列 - 01] 如何進行數值的累加?
## 前言
今天收到一個如何進行金額累加的問題,問題如下:
> 問:請問我要如何拿到上個月的 carry forward amount + 這個月的 amount?
而問題的截圖中的意思大致如下:
> | Project Name | Transaction Date | Amount (RM) | C/F Amount (RM) |
> | ------------ | ----------------------- | ----------- | --------------- |
> | AAA Project | 2019-11-01 ~ 2019-11-30 | 10,000.00 | 10,000.00 |
> | AAA Project | 2019-12-01 ~ 2019-12-31 | 2,000.00 | 12,000.00 |
> | AAA Project | 2020-01-01 ~ 2020-01-31 | 3,000.00 | 15,000.00 |
> | AAA Project | 2020-02-01 ~ 2020-02-29 | 6,000.00 | 21,000.00 |
## 思路
在看了問題的描述與截圖並了解該問題之後,我的理解是該問題需要針對"**金額**"按"**交易時間**”做”**累加**”計算
另外,在截圖中,針對結果的條件範圍有:
* Project Name
* Account Name
* Transaction Date
而我的想法會是,既然我們已經知道結果的條件範圍,那麼我們就可以先針對條件下手。
所以,我的第一個想法會是先將 "**Transaction Date**" 按照"**年月**"分群,也就是把 "Transaction Date" 給濃縮成 "Transaction Year Month",所以基本的交易結果會是如下:
> | Project Name | Transaction YearMonth | Amount (RM) |
> | ------------ | --------------------- | ----------- |
> | AAA Project | 201911 | 10,000.00 |
> | AAA Project | 201912 | 2,000.00 |
> | AAA Project | 202001 | 3,000.00 |
> | AAA Project | 202002 | 6,000.00 |
## 解法
在將交易資料按照"**年月**"分群後,那麼要進行金額累加就可以簡單的做到,也就是將"**每個交易年月"之前(包含"該交易年月")** 的金額做加總就可以了,那麼,上程式!
### 程式碼
``` SQL
/* 建立資料表 */
CREATE TABLE #PM_ProjectLedger (
ProjectID VARCHAR(2) NOT NULL,
AccountCode VARCHAR(10) NOT NULL,
CreditAmount DECIMAL(18, 0) NOT NULL DEFAULT(0),
DebitAmount DECIMAL(18, 0) NOT NULL DEFAULT(0),
TransactionDate DATE
);
GO
CREATE TABLE #PM_Project (
ProjectID VARCHAR(2) NOT NULL,
ProjectName VARCHAR(50) NOT NULL
);
GO
/* 寫入專案資料 */
INSERT INTO #PM_Project
(ProjectID
,ProjectName
)
VALUES('90', 'RESORT HOMES SL')
,('0', 'Internal Project');
GO
/* 寫入交易資料 */
INSERT INTO #PM_ProjectLedger
(ProjectID
, AccountCode
, CreditAmount
, DebitAmount
, TransactionDate)
VALUES('90', 'PD-SC-0001', 0, 1000000, CONVERT(DATE, '2019/11/03'))
,('90', 'PD-SC-0001', 0, 12000000, CONVERT(DATE, '2019/11/07'))
,('90', 'PD-SC-0001', 0, 3000000, CONVERT(DATE, '2019/11/12'))
,('90', 'PD-SC-0001', 0, 275000, CONVERT(DATE, '2019/11/23'))
,('90', 'PD-SC-0001', 0, 631000, CONVERT(DATE, '2019/12/02'))
,('90', 'PD-SC-0001', 0, 852000, CONVERT(DATE, '2019/12/03'))
,('90', 'PD-SC-0001', 0, 4650000, CONVERT(DATE, '2019/12/24'))
,('90', 'PD-SC-0001', 0, 12000000, CONVERT(DATE, '2019/12/28'))
,('90', 'PD-SC-0001', 0, 3000000, CONVERT(DATE, '2020/01/15'))
,('90', 'PD-SC-0001', 0, 275000, CONVERT(DATE, '2020/01/17'))
,('90', 'PD-SC-0001', 0, 631000, CONVERT(DATE, '2020/01/31'))
,('90', 'PD-SC-0001', 0, 852000, CONVERT(DATE, '2020/02/04'))
,('90', 'PD-SC-0001', 0, 4650000, CONVERT(DATE, '2020/02/09'))
,('90', 'PD-SC-0001', 0, 12000000, CONVERT(DATE, '2020/02/18'))
,('90', 'PD-SC-0001', 0, 3000000, CONVERT(DATE, '2020/02/21'))
,('90', 'PD-SC-0001', 0, 275000, CONVERT(DATE, '2020/02/29'))
,('90', 'PD-SC-0001', 0, 631000, CONVERT(DATE, '2020/03/05'))
,('90', 'PD-SC-0001', 0, 852000, CONVERT(DATE, '2020/03/11'))
,('90', 'PD-SC-0001', 0, 4650000, CONVERT(DATE, '2020/03/13'))
,('90', 'PD-SC-0001', 0, 12000000, CONVERT(DATE, '2020/03/15'))
,('90', 'PD-SC-0001', 0, 3000000, CONVERT(DATE, '2020/03/16'))
,('90', 'PD-SC-0001', 0, 275000, CONVERT(DATE, '2020/03/21'))
,('90', 'PD-SC-0001', 0, 631000, CONVERT(DATE, '2020/03/27'))
,('90', 'PD-SC-0001', 0, 852000, CONVERT(DATE, '2020/03/28'))
,('90', 'PD-SC-0001', 0, 4650000, CONVERT(DATE, '2020/03/30'))
;
GO
/* 產生 View */
;WITH vw_PM_ProjectLedgerWithProject_AccountCode_Transaction (ProjectName, AccountCode, TransactionYearMonth, Amount)
AS (SELECT ProjectName,
AccountCode,
TransactionYearMonth,
ROUND((SUM(Balance) / 1000000), 2) AS Amount
FROM
(
SELECT p.ProjectName,
pl.AccountCode,
CONVERT(INT, CONVERT(VARCHAR(6), TransactionDate, 112)) AS TransactionYearMonth,
(DebitAmount - CreditAmount) AS Balance
FROM #PM_Project AS p
INNER JOIN #PM_ProjectLedger AS pl
ON p.ProjectID = pl.ProjectID
) AS t
GROUP BY ProjectName,
AccountCode,
TransactionYearMonth
)
SELECT ReportingMonth.ProjectName
, ReportingMonth.TransactionYearMonth
, ReportingMonth.Amount AS [Amount (RM)]
, SUM(PastMonth.Amount) AS [C/F Amount (RM)]
FROM vw_PM_ProjectLedgerWithProject_AccountCode_Transaction AS ReportingMonth
LEFT JOIN vw_PM_ProjectLedgerWithProject_AccountCode_Transaction AS PastMonth
ON ReportingMonth.ProjectName = PastMonth.ProjectName
AND ReportingMonth.AccountCode = PastMonth.AccountCode
AND ReportingMonth.TransactionYearMonth >= PastMonth.TransactionYearMonth
WHERE ReportingMonth.ProjectName = 'RESORT HOMES SL'
AND ReportingMonth.AccountCode = 'PD-SC-0001'
AND ReportingMonth.TransactionYearMonth = 201912
GROUP BY ReportingMonth.ProjectName
, ReportingMonth.TransactionYearMonth
, ReportingMonth.Amount
ORDER BY ReportingMonth.ProjectName
, ReportingMonth.TransactionYearMonth;
```
### 結果
我們可以從下列的表格中看到我們的結果符合一開始的問題,將金額按”**交易時間**”做”**累加**”計算,並且累加的金額也正確的計算出來了。
| Project Name | Transaction Year Month | Amount (RM) | C/F Amount (RM) |
| --------------- | ----------------------- | ----------- | --------------- |
| RESORT HOMES SL | 201911 | 16.280000 | 16.280000 |
| RESORT HOMES SL | 201912 | 18.130000 | 34.410000 |
| RESORT HOMES SL | 202001 | 3.910000 | 38.320000 |
| RESORT HOMES SL | 202002 | 20.780000 | 59.100000 |
| RESORT HOMES SL | 202003 | 27.540000 | 86.640000 |
如果單純指定"**交易年月**"(例如:2019-12),得到的結果也是正確的。
``` SQL
ReportingMonth.TransactionYearMonth = 201912
```
| Project Name | Transaction Year Month | Amount (RM) | C/F Amount (RM) |
| --------------- | ----------------------- | ----------- | --------------- |
| RESORT HOMES SL | 201912 | 18.130000 | 34.410000 |
## 結語
對於今天的這個"**金額累加**"問題,個人覺得會建議在解決問題前,我們應該先撇開 SQL 的語法,而該思考的方向是先去了解"**累加**"的如何**運作**的,在了解之後,才再回來思考該如何用 SQL 語法將結果給撈出來,希望以上的說明可以幫助到大家了解並學會如何通過 SQL 做"**數值累加**",當然,數值累加不單單只能針對"**金額**"做,對於客戶的"**點數累加**"、"**哩程數累加**"也都是一樣的喔!
**如果你/妳有任何的想法或是有其他的解決方法,都歡迎底下留言並一起討論喔!**
###### tags: `SQL` `累加` `Cumulative`