Search code examples
sqlsql-serverscd

SQL Summary of Status total values by Month of Type2 SC Dimension


I have a voucher status history table as a type2 slowly changing dimension table I am trying to get the summary total value of each status by each month before a particular date. This is my schema and insert code:

CREATE TABLE #HDimVouchers(
       [HVoucherKey] [bigint] IDENTITY(1,1) NOT NULL,
       [Voucher_id] [bigint] NOT NULL,
       [VoucherStatusKey] [int] NOT NULL,
       [Voucher_amt] [decimal](18, 2) NULL,    
       [DateStatusStart] [date] NULL,       
       [DateStatusEnd] [date] NULL
     
)
--drop table #HDimVouchers
insert #HDimVouchers 
values
(10,2,10.00,'2019-01-01','2019-02-15'),
(10,4,10.00,'2019-02-16',null),
(13,4,10.00,'2019-01-10',null),
(11,2,15.00,'2019-01-01',null),
(12,2,20.00,'2019-03-12','2019-03-12'),
(12,4,20.00,'2019-03-13',null),
(15,2,205.00,'2019-05-25','2020-04-24'),
(15,6,205.00,'2020-04-25',null),
(21,2,100.00,'2019-02-16',null)

I would like to get a summary to total value by year-month by voucherstatuskey something like the below:

[Year-Month] [VoucherStatusKey] [Amount]
201901 2 25
201901 4 10
201902 2 100
201902 4 10
201903 4 20
201905 2 205
201906 2 205
201907 2 205
201908 2 205
201909 2 205
201910 2 205
201911 2 205
201912 2 205
202001 2 205
202002 2 205
202003 2 205

I have had many attempts to get the data as above, but I am struggling to get the correct format and values. Below is something I have tried

SELECT  convert(nvarchar(4),Year([DateStatusStart])) + RIGHT('00' + CONVERT(NVARCHAR(2), DATEPART(Month, [DateStatusStart])), 2)
,[VoucherStatusKey]
,SUM([Voucher_amt]) OVER (PARTITION BY Year([DateStatusStart]),Month([DateStatusStart]), [VoucherStatusKey] ORDER BY [DateStatusStart]) AS running_total 
FROM #HDimVouchers where [DateStatusStart] < '2020-03-31';

Solution

  • Let me assume that you want the value at the end of the month. Then, you can take the following approach:

    • Generate all appropriate months for each voucher.
    • Use a join to bring in the appropriate value.

    For the first part, you could use a tally or calendar table if one is available. However a recursive CTE is also convenient:

    with vdates as (
          select voucher_id, eomonth(min(DateStatusStart)) as eom
          from HDimVouchers
          group by voucher_id
          union all
          select voucher_id, eomonth(dateadd(month, 1, eom))
          from vdates
          where eom < '2020-03-01'
         )
    select vd.*, hv.Voucher_amt
    from vdates vd join
         HDimVouchers hv
         on hv.voucher_id = vd.voucher_id and
            vd.eom >= hv.DateStatusStart and
            (vd.eom <= hv.DateStatusEnd or hv.DateStatusEnd is null)
    order by vd.eom, vd.voucher_id;
    

    Here is a db<>fiddle.