Code:
use tempdb;
go
DROP TABLE IF EXISTS Calendar;
DROP TABLE IF EXISTS Accounts;
DROP TABLE IF EXISTS Transactions;
CREATE TABLE Calendar(TheDate DATE PRIMARY KEY);
/* this doesn't make sense... it's missing the OpenDate */
CREATE TABLE Accounts (
AccountID INT PRIMARY KEY,
StartingBalance DECIMAL(8,2) NOT NULL
);
CREATE TABLE Transactions (
TransactionID INT IDENTITY,
TransactionDate DATE NOT NULL,
AccountID INT NOT NULL,
Amount DECIMAL(8,2)
);
GO
INSERT INTO Accounts(AccountID, StartingBalance)
VALUES (14,376.94),(15,-18629.34), (22, 200.00);
DECLARE @TheDate DATE = '2025-01-01';
WHILE @TheDate < '2025-01-12'
BEGIN
INSERT INTO Calendar(TheDate)
VALUES (@TheDate);
SET @TheDate = DATEADD(day,1,@TheDate);
END;
INSERT INTO Transactions(TransactionDate, AccountID, Amount)
VALUES
('2025-01-02', 15, -1000.00),
('2025-01-02', 15, -50.00),
('2025-01-02', 15, -169.00),
('2025-01-04', 15, -700.00),
('2025-01-06', 22, -30.00),
('2025-01-07', 22, -80.00),
('2025-01-07', 15, -15800.00),
('2025-01-07', 15, -427.41),
('2025-01-08', 14, 61477.00),
('2025-01-08', 14, -36775.75);
/* get the daily totals */
/* now add the running total */
SELECT z.AccountID, z.TheDate, z.StartingBalance, z.TransactionID, z.AccountID, z.Amount AS TransactionAmount, z.rt AS RunningBalance
FROM
(
SELECT
daily.AccountID,
daily.TheDate,
daily.StartingBalance,
daily.TransactionID,
daily.Amount,
rt = daily.StartingBalance + SUM(daily.Amount) OVER (PARTITION BY daily.AccountID
ORDER BY daily.TheDate
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
FROM
(
/* Now add transactions daily */
SELECT xj.TheDate, xj.AccountID, xj.StartingBalance, t.TransactionID, t.Amount
FROM /* cross join forces there to exist a date for every account */
(SELECT a.AccountID, c.TheDate, a.StartingBalance
FROM Accounts a
CROSS JOIN Calendar c) xj
LEFT JOIN Transactions t
ON xj.AccountID = t.AccountID AND xj.TheDate = t.TransactionDate
) daily
) z
Oh hey look! Past Me was really nice and left some helpful comments in there... "cross join forces there to exist a date for every account". Basically, a cross join does relational multiplication - it returns every combination of the values in Accounts and the values in Calendar. So then all you have to do is a running total on top of that.