From SQL to SPL: Calculate monthly account balance and fill in missing dates
There is a transaction table for the asset accounts in the MS SQL database, with dates that are not consecutive.
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 |
Now we need to calculate the balance of each account for each month from January 2021 at the beginning of the period to April 2024 at the end of the period, and fill in the missing months.
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 Solution:
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 does not have a convenient method to generate a months sequence, so nested queries and window functions are required, and the code is very complex.
SPL provides functions for generating dates sequence, including consecutive months.
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: Query the database, group by account and date of the first day of each month, and calculate the monthly amount changes.
A2: Generate a continuous sequence consisting of the first day of each month. periods() generates a dates sequence, and @m represents the interval unit in month.
A3: Cross joint the account and date sequence.
A4: Left join the last cross-join result with A1.
A5: When the current account remains unchanged compared to the previous record, the current month's balance equals the change in the current month's amount plus the previous month's balance; When the account changes, the current month balance is reset to the current month amount change.
Question source:https://stackoverflow.com/questions/78296238/aggregate-financial-transactions-into-monthly-balances
SPL Official Website 👉 https://www.scudata.com
SPL Feedback and Help 👉 https://www.reddit.com/r/esProcSPL
SPL Learning Material 👉 https://c.scudata.com
SPL Source Code and Package 👉 https://github.com/SPLWare/esProc
Discord 👉 https://discord.gg/2bkGwqTj
Youtube 👉 https://www.youtube.com/@esProc_SPL