从 SQL 到 SPL:计算帐户每月余额,补齐缺失日期
MSSQL 数据库有个资产账户的流水表,日期不连续。
Name |
Date |
Debit |
Credit |
A |
2021-01-01 |
10 |
0 |
A |
2021-01-01 |
9 |
0 |
A |
2021-02-01 |
11 |
0 |
A |
2021-03-01 |
0 |
50 |
A |
2021-04-01 |
30 |
0 |
B |
2021-01-01 |
10 |
0 |
B |
2022-02-01 |
0 |
12 |
B |
2022-03-01 |
0 |
50 |
B |
2024-04-01 |
3 |
0 |
现在要统计从期初 2021 年 1 月到期末 2024 年 4 月每个账户每个月的余额,缺失的月份要补齐。
Name |
y |
m |
Blance |
A |
2021 |
1 |
-19 |
A |
2021 |
2 |
-30 |
A |
2021 |
3 |
20 |
A |
2021 |
4 |
-10 |
A |
2021 |
5 |
-10 |
… |
… |
… |
… |
A |
2024 |
3 |
-10 |
A |
2024 |
4 |
-10 |
B |
2021 |
1 |
-10 |
B |
2021 |
2 |
-10 |
… |
… |
… |
… |
B |
2022 |
1 |
-10 |
B |
2022 |
2 |
2 |
B |
2022 |
3 |
52 |
B |
2022 |
4 |
52 |
… |
… |
… |
… |
B |
2024 |
3 |
52 |
B |
2024 |
4 |
49 |
SQL 解法:
WITH Accounts AS (
SELECT DISTINCT Name FROM trans
),
Months AS (
SELECT DATEADD(MONTH, n, '2021-01-01') AS MonthStart
FROM (
SELECT TOP (DATEDIFF(MONTH, '2021-01-01', '2024-04-01') + 1)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS n
FROM master.dbo.spt_values
) AS Numbers
),
AccountMonths AS (
SELECT a.Name, m.MonthStart
FROM Accounts a
CROSS JOIN Months m
),
Changes as (
SELECT Name, DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0) as ym,
sum(Credit - Debit) as change
FROM trans group by Name, DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)
),
fullChanges as (
SELECT A.Name,A.MonthStart,c.change
FROM AccountMonths a left join Changes c
on a.Name=c.Name and a.MonthStart=c.ym
)
SELECT
Name,YEAR(MonthStart) AS Y,MONTH(MonthStart) AS M,
SUM(change) OVER (PARTITION BY Name ORDER BY MonthStart) AS balance
FROM fullChanges
SQL没有方便的方法生成月份序列,要用嵌套查询+窗口函数,代码非常复杂。
SPL提供了生成日期序列的函数,包括连续月份。
A |
|
1 |
=mssql.query("select Name, DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)as ym,sum(Credit - Debit) as change from trans group by Name, DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)”) |
2 |
=periods@m(date("2021-01-01"),date("2024-04-01"),1) |
3 |
=xjoin(A1.id(Name):Name; A2:ym) |
4 |
=A3.join(Name:ym, A1:Name:ym,change) |
5 |
=A4.new(Name,year(ym):y,month(ym):m,change+if(Name==Name[-1] , Balance[-1]):Balance) |
A1:查询数据库,按账户、每月第1天的日期分组,统计每月金额变化。
A2:生成每月第一天组成的连续序列。perionds生成日期序列,@m表示间隔单位为月份。
A3:将账户和日期序列进行叉乘。
A4:将叉乘结果和A1左关联。
A5:当前账号与上一条记录相比不变时,当月余额=当月金额变化+上个月的余额;账号变化时,当月余额重置为当月金额变化。
问题来源:https://stackoverflow.com/questions/78296238/aggregate-financial-transactions-into-monthly-balances
英文版 https://c.scudata.com/article/1735808086311