sqlsql-servercumulative-sum

Cumulative amount by report dates


I have two tables

sales

id colour payment_date amount
1 red 2023-01-04 2
1 green 2023-01-04 5
2 green 2023-01-04 1
2 green 2023-02-04 1
2 green 2023-03-04 1
2 green 2023-04-04 1

report_dates

date
2022-12-31
2023-01-31
2023-02-28
2023-03-31
2023-04-30
2023-05-31
2023-06-30

My aim is cumulative amount by report dates

I get cumulative amount by EOMONTH of payment_dates

SELECT
    sales.id  
    ,EOMONTH(sales.payment_date)  
    ,sales.colour
    ,SUM(sales.amount) OVER (PARTITION BY sales.id, sales.colour ORDER BY EOMONTH(sales.payment_date)) 
FROM sales
ORDER BY pl.AGREEMENT_RK, pl.OPER_DATE

How to join with report dates and get report like this? EDITED: report dates before sales date doesn't needed

id colour date cum_amount
1 red 2023-01-31 2
1 red 2023-02-28 2
1 red 2023-03-31 2
1 red 2023-04-30 2
1 red 2023-05-31 2
1 red 2023-06-30 2
1 green 2023-01-31 5
1 green 2023-02-28 5
1 green 2023-03-31 5
1 green 2023-04-30 5
1 green 2023-05-31 5
1 green 2023-06-30 5
2 green 2023-01-31 1
2 green 2023-02-28 2
2 green 2023-03-31 3
2 green 2023-04-30 4
2 green 2023-05-31 4
2 green 2023-06-30 4

Solution

  • Try by calculating the SUM in a separate CTE and eventually UNION with the missing ones:

    with sales(id, colour, payment_date, amount) as (
        select 1, 'red', cast( '2023-01-04' as date), 2  union all
        select 1, 'green', cast( '2023-01-04'as date), 5  union all
        select 2, 'green', cast( '2023-01-04'as date), 1  union all
        select 2, 'green', cast('2023-02-04'as date), 1  union all
        select 2, 'green', cast( '2023-03-04'as date), 1  union all
        select 2, 'green', cast( '2023-04-04' as date), 1 
    ),
    report_dates(dat) as (
        select cast( '2022-12-31' as date) union all
        select cast( '2023-01-31' as date) union all
        select cast( '2023-02-28' as date) union all
        select cast( '2023-03-31' as date) union all
        select cast( '2023-04-30' as date) union all
        select cast( '2023-05-31' as date) union all
        select cast( '2023-06-30'as date) 
    ),
    stats as (
        select s.id, s.colour, r.dat, 
            sum(s.amount) over(partition by s.colour, s.id order by r.dat) as cum_amount
        from (select dat, lag(dat) over(order by dat) as from_dat from report_dates) r
        join sales s on s.payment_date between coalesce(r.from_dat, cast('0001-01-01'as date)) and r.dat
    )
    select * from (
        select s.id, s.colour, r.dat, s.cum_amount
        from report_dates r
        join stats s on s.dat <= r.dat
        
        union all
        
        select s.id, s.colour, r.dat, 0
        from report_dates r
        , (select distinct id, colour from sales) s
        where not exists(select 1 from stats st where st.dat <= r.dat and st.id = s.id and st.colour = s.colour) 
    ) t
    order by id, colour desc, dat
    ;
    

    https://dbfiddle.uk/ILZn5lFc