问题描述
我有 2 个维度表和 1 个事实表,如下所示:
user_dim
user_id | 用户名 | user_joining_date |
---|---|---|
1 | 史蒂夫 | 2013-01-04 |
2 | 亚当 | 2012-11-01 |
3 | 约翰 | 2013-05-05 |
4 | 托尼 | 2012-01-01 |
5 | 丹 | 2010-01-01 |
6 | 亚历克斯 | 2019-01-01 |
7 | 金 | 2019-01-01 |
bundle_dim
bundle_id | bundle_name | bundle_type | bundle_cost_per_day |
---|---|---|---|
101 | 电影和电视 | 主要 | 5.5 |
102 | 电视和体育 | 主要 | 6.5 |
103 | 烹饪 | 主要 | 7 |
104 | 体育和新闻 | 主要 | 5 |
105 | 儿童电影 | 额外 | 2 |
106 | 儿童教育 | 额外 | 3.5 |
107 | 西班牙新闻 | 额外 | 2.5 |
108 | 西班牙电视和体育 | 额外 | 3.5 |
109 | 旅游 | 额外 | 2 |
plans_fact
user_id | bundle_id | bundle_start_date | bundle_end_date |
---|---|---|---|
1 | 101 | 2019-10-10 | 2020-10-10 |
2 | 107 | 2020-01-15 | (空) |
2 | 106 | 2020-01-15 | 2020-12-31 |
2 | 101 | 2020-01-15 | (空) |
2 | 103 | 2020-01-15 | 2020-02-15 |
1 | 101 | 2020-10-11 | (空) |
1 | 107 | 2019-10-10 | 2020-10-10 |
1 | 105 | 2019-10-10 | 2020-10-10 |
4 | 101 | 2021-01-01 | 2021-02-01 |
3 | 104 | 2020-02-17 | 2020-03-17 |
2 | 108 | 2020-01-15 | (空) |
4 | 102 | 2021-01-01 | (空) |
4 | 103 | 2021-01-01 | (空) |
4 | 108 | 2021-01-01 | (空) |
5 | 103 | 2020-01-15 | (空) |
5 | 101 | 2020-01-15 | 2020-02-15 |
6 | 101 | 2021-01-01 | 2021-01-17 |
6 | 101 | 2021-01-20 | (空) |
6 | 108 | 2021-01-01 | (空) |
7 | 104 | 2020-02-17 | (空) |
7 | 103 | 2020-01-17 | 2020-01-18 |
1 | 102 | 2020-12-11 | (空) |
2 | 106 | 2021-01-01 | (空) |
7 | 107 | 2020-01-15 | (空) |
注意:NULL bundle_end_date 指的是活动订阅。
用户活跃天数可以计算为:bundle_end_date - bundle_start_date
(对于给定的捆绑包)
每个用户的总收入可以计算为:total no. of active days * bundle rate per day
以下是我对每位用户的总体收入的了解:
select pf.user_id,sum(datediff(day,pf.bundle_start_date,coalesce(pf.bundle_end_date,getdate())) * bd.price_per_day) total_cost_per_bundle
from plans_fact pf
inner join bundle_dim bd on bd.bundle_id = pf.bundle_id
group by pf.user_id
order by pf.user_id;
解决方法
您需要一个“年份”表来帮助将每个跨多年的行解析为单独的年份。对于每一年,您还需要重新计算开始和结束日期。这就是我在下面代码的 yearParsed
cte 中所做的。我将年份硬编码到创建 y
的连接语句中。您可能会采用不同的方法,但无论您如何获得这些值,这些值都会起作用。
在那之后,与您之前所做的差不多,只是将年份列添加到您的分组中。
除此之外,我所做的只是将空合并逻辑移到 cte 以使整体逻辑更简单。
with yearParsed as (
select pf.*,y.year,startDt = iif(pf.bundle_start_date > y.startDt,pf.bundle_start_date,y.startDt),endDt = iif(ap.bundle_end_date < y.endDt,ap.bundle_end_date,y.endDt)
from plans_fact pf
cross apply (select bundle_end_date = isnull(pf.bundle_end_date,getdate())) ap
join (values
(2019,'2019-01-01','2019-12-31'),(2020,'2020-01-01','2020-12-31'),(2021,'2021-01-01','2021-12-31')
) y (year,startDt,endDt)
on pf.bundle_start_date <= y.endDt
and ap.bundle_end_date >= y.startDt
)
select yp.user_id,yp.year,total_cost_per_bundle = sum(datediff(day,yp.startDt,yp.endDt) * bd.bundle_cost_per_day)
from yearParsed yp
join bundle_dim bd on bd.bundle_id = yp.bundle_id
group by yp.user_id,yp.year
order by yp.user_id,yp.year;
现在,如果这很常见,您可能应该为“年份”表创建一个基表。但是,如果它不常见,但对于此报告,您不想继续返回将年份信息硬编码到 y
表中,您可以这样做:
declare @yearTable table (
year int,startDt char(10),endDt char(10)
);
with y as (
select year = year(min(pf.bundle_start_date))
from @plans_fact pf
union all
select year + 1
from y
where year < year(getdate())
)
insert @yearTable
select year,startDt = convert(char(4),year) + '-01-01',endDt = convert(char(4),year) + '-12-31'
from y;
它会为您创建合适的年份。但是,如果您经常有这种或类似的需求,您就会明白为什么创建基表可能是首选。