问题描述
我有一个凭证状态历史记录表作为 type2 缓慢变化的维度表我试图在特定日期之前的每个月获取每个状态的汇总总值。这是我的架构和插入代码:
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,'2019-02-16',null),(13,'2019-01-10',(11,15.00,(12,20.00,'2019-03-12','2019-03-12'),'2019-03-13',(15,205.00,'2019-05-25','2020-04-24'),6,'2020-04-25',(21,100.00,null)
我想通过凭证状态键按年-月获得总价值的摘要,如下所示:
[年-月] | [VoucherStatusKey] | [数量] |
---|---|---|
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 |
我曾多次尝试获取上述数据,但我很难获得正确的格式和值。下面是我尝试过的东西
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';
解决方法
让我假设您想要月末的值。然后,您可以采取以下方法:
- 为每张代金券生成所有合适的月份。
- 使用
join
引入适当的值。
对于第一部分,您可以使用计数表或日历表(如果有)。然而,递归 CTE 也很方便:
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 是一个 dbfiddle。
,我对此的看法是:
;with [dates] as (
select YEAR(MIN([DateStatusStart]))*100+MONTH(MIN([DateStatusStart])) [YM] from #HDimVouchers
union all
select case when ([dates].[YM] % 100) = 12 then [dates].[YM] + 100 - 11 else [dates].[YM] + 1 end from [dates] where [YM] < 202112
),[dimkeys] as (
select
[Voucher_id],YEAR(MIN([DateStatusStart]))*100+MONTH(MIN([DateStatusStart])) [DateStatusStart],YEAR(MAX(ISNULL([DateStatusEnd],DATEFROMPARTS(2999,12,31))))*100+MONTH(MAX(ISNULL([DateStatusEnd],31)))) [DateStatusEnd]
from [#HDimVouchers] group by [Voucher_id]
),[map] as (
select
[dimkeys].[Voucher_id],[dates].[YM],COALESCE(
MAX([d].[DateStatusStart]),(select MAX([i].[DateStatusStart]) from [#HDimVouchers] [i] where [i].[Voucher_id] = [dimkeys].[Voucher_id] and YEAR([i].[DateStatusStart])*100+MONTH([i].[DateStatusStart]) < [dates].[YM]),(select MIN([i].[DateStatusStart]) from [#HDimVouchers] [i] where [i].[Voucher_id] = [dimkeys].[Voucher_id])
) [MappingDate]
from [dates]
cross join [dimkeys]
left join [#HDimVouchers] [d] on [d].[Voucher_id] = [dimkeys].[Voucher_id] and YEAR([d].[DateStatusStart])*100+MONTH([d].[DateStatusStart]) = [dates].[YM]
where [dates].[YM] >= [dimkeys].[DateStatusStart] and [dates].[YM] <= [dimkeys].[DateStatusEnd]
group by [dimkeys].[Voucher_id],[dates].[YM]
)
select [map].[YM],[fact].[VoucherStatusKey],SUM([fact].[Voucher_amt]) [Sum]
from [map] join [#HDimVouchers] [fact] on [fact].[Voucher_id] = [map].[Voucher_id] and [fact].[DateStatusStart] = [map].[MappingDate]
group by [map].[YM],[fact].[VoucherStatusKey]
order by [YM],[VoucherStatusKey];
所以:
- 获取从开始到结束的所有年月值
- 获取所有不同键及其总体最小/最大日期(也就是成员存在时)
- 交叉加入他们以获取每个年月的每个键的条目(在成员的生命周期内)
- 添加应该用于映射的日期(这用于决定将在年-月中更改的成员添加到哪个月份)
- 然后才通过 SCD 类型 2 属性将其与完整维度和组连接起来
更新
对于大表,您可以将其拆分为多个临时表。表而不是全力以赴的 CTE。这通常对性能有很大帮助。
select *,YEAR([DateStatusStart])*100+MONTH([DateStatusStart]) [YmStart],YEAR([DateStatusEnd])*100+MONTH([DateStatusEnd]) [YmEnd]
into [#withYm]
from [#HDimVouchers];
;with [dates] as (
select MIN([YmStart]) [YM] from [#withYm]
union all
select case when ([dates].[YM] % 100) = 12 then [dates].[YM] + 100 - 11 else [dates].[YM] + 1 end from [dates] where [YM] < 202112
),MIN([YmStart]) [YmStart],MAX(ISNULL([YmEnd],299912)) [YmEnd]
from [#withYm]
group by [Voucher_id]
)
select
[dimkeys].[Voucher_id],[dates].[YM]
into [#all]
from [dates]
cross join [dimkeys]
where [dates].[YM] >= [dimkeys].[YmStart] and [dates].[YM] <= [dimkeys].[YmEnd]
;with [map] as (
select
[#all].[Voucher_id],[#all].[YM],ISNULL(
MAX([d].[DateStatusStart]),(select MAX([i].[DateStatusStart]) from [#withYm] [i] where [i].[Voucher_id] = [#all].[Voucher_id] and [i].[YmStart] < [#all].[YM])
) [MappingDate]
from [#all]
left join [#HDimVouchers] [d] on [d].[Voucher_id] = [#all].[Voucher_id] and YEAR([d].[DateStatusStart])*100+MONTH([d].[DateStatusStart]) = [#all].[YM]
group by [#all].[Voucher_id],[#all].[YM]
)
select [map].[YM],[VoucherStatusKey];