Skip to content
Advertisement

SQL – get rid of the nested aggregate select

There is a table Payment, which for example tracks the amount of money user puts into account, simplified as

===================================
Id | UserId |  Amount |   PayDate |
===================================
 1 |     42 |      11 |  01.02.99 |
 2 |     42 |      31 |  05.06.99 |
 3 |     42 |      21 |  04.11.99 |
 4 |     24 |      12 |  05.11.99 |

What is need is to receive a table with balance before payment moment, eg:

===============================================
Id | UserId |   Amount |   PayDate |  Balance |
===============================================
 1 |     42 |       11 |  01.02.99 |        0 |
 2 |     42 |       31 |  05.06.99 |       11 |
 3 |     42 |       21 |  04.11.99 |       42 |
 4 |     24 |       12 |  05.11.99 |        0 |

Currently the select statement looks something like

SELECT 
    Id,
    UserId,
    Amount,
    PaidDate,
    (SELECT sum(amount) FROM Payments nestedp 
      WHERE nestedp.UserId = outerp.UserId AND
            nestedp.PayDate < outerp.PayDate) as Balance
FROM
    Payments outerp 

How can I rewrite this select to get rid of the nested aggregate selection? The database in question is SQL Server 2019.

Advertisement

Answer

Thanks to other participants’ leads I came up with a query that (seems) to work:

SELECT 
    Id,
    UserId,
    Amount,
    PayDate,
    COALESCE(sum(Amount) over (partition by UserId 
                                order by PayDate 
                                rows between unbounded preceding and 1 preceding), 0) as Balance
FROM
    Payments
ORDER BY
    UserId, PayDate

Lots of related examples can be found here

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement