I am trying to get a balance history on the account using SQL. My table in PostgreSQL looks like this:
id sender_id recipient_id amount_money --- ----------- ---------------------- ----------------- 1 1 2 60.00 2 1 2 15.00 3 2 1 35.00
so the user with id number 2 currently has 40 dollars in his account. I would like to get this result using sql:
[60, 75, 40]
Is it possible to do something like this using sql in postgres?
Advertisement
Answer
To get a rolling balance, you can SUM
the amounts (up to and including the current row) based on whether the id
was the recipient or sender:
SELECT id, sender_id, recipient_id, amount_money, SUM(CASE WHEN recipient_id = 2 THEN amount_money WHEN sender_id = 2 THEN -amount_money END) OVER (ORDER BY id) AS balance FROM transactions
Output:
id sender_id recipient_id amount_money balance 1 1 2 60.00 60.00 2 1 2 15.00 75.00 3 2 1 35.00 40.00
If you want an array, you can use array_agg
with the above query as a derived table:
SELECT array_agg(balance) FROM ( SELECT SUM(CASE WHEN recipient_id = 2 THEN amount_money WHEN sender_id = 2 THEN -amount_money END) OVER (ORDER BY id) AS balance FROM transactions ) t
Output:
[60,75,40]
If you want to be more sophisticated and support balances for multiple accounts, you need to split the initial data into account ids, adding when the id is the recipient and subtracting when the sender. You can use CTE
s to generate the appropriate data:
WITH trans AS ( SELECT id, sender_id AS account_id, -amount_money AS amount FROM transactions UNION ALL SELECT id, recipient_id AS account_id, amount_money AS amount FROM transactions ), balances AS ( SELECT id, account_id, ABS(amount), SUM(amount) OVER (PARTITION BY account_id ORDER BY id) AS balance FROM trans ) SELECT account_id, ARRAY_AGG(balance) AS bal_array FROM balances GROUP BY account_id
Output:
account_id bal_array 1 [-60,-75,-40] 2 [60,75,40]