第 4 题 打折日期交叉问题

1、题目要求

如下为平台商品促销数据: 字段为品牌,打折开始日期,打折结束日期

计算每个品牌总的打折销售天数,注意其中的交叉日期,比如 vivo 品牌,第一次活动时

间为 2021-06-05 到 2021-06-15,第二次活动时间为 2021-06-09 到 2021-06-21 其中 9 号到 15

号为重复天数,只统计一次,即 vivo 总打折天数为 2021-06-05 到 2021-06-21 共计 17 天。

brand     stt       edt
oppo;2021-06-05;2021-06-09
oppo;2021-06-11;2021-06-21
vivo;2021-06-05;2021-06-15
vivo;2021-06-09;2021-06-21
redmi;2021-06-05;2021-06-21
redmi;2021-06-09;2021-06-15
redmi;2021-06-17;2021-06-26
huawei;2021-06-05;2021-06-26
huawei;2021-06-09;2021-06-15
huawei;2021-06-17;2021-06-21

2、建表和加载数据

create table if not exists test4(
brand string,
stt string,
edt string
)row format delimited fields terminated by ";";

load data local inpath '/opt/test/t4.txt' overwrite into table test4;

3、分析

        1)如果没有重复的打折时间,那就直接每次打折结束时间减开始时间加一在聚合求和

但是,如下数据,存在交叉打折的情况

redmi 2021-06-05 2021-06-21
redmi 2021-06-09 2021-06-15
redmi 2021-06-17 2021-06-26

这时我们判断当前行开始时间是不是小于组内之前全部行的最大结束时间,如果小于那就将当前行的开始时间替换成之前行的最大结束时间加1(不加1就重复天了)

比如:第二行 开始时间2021-06-09 那第二行之前的最大结束时间就是2021-06-21 加1 2021-06-22
    第三行 开始时间2021-06-17   那第三行之前的最大结束时间就是2021-06-21 加1 2021-06-22  
redmi 2021-06-05 2021-06-21  null
redmi 2021-06-09 2021-06-15 2021-06-22
redmi 2021-06-17 2021-06-26 2021-06-22

然后我们在当前行的结束时间减去移下来的第四列数据,大于0就是真实的天数,小于零说明被包含了

        到这整个思路就清楚了,下面就是分步骤来实现

        2)将第一行到当前行的最大结束时间移到当前行作为第四列(首先数据都是按照开始时间排好序的,如果没有排好序可以自己先排序)

max(edt) over(partition by brand order by stt rows between UNBOUNDED PRECEDING and 1 PRECEDING) maxedt
select
brand,stt,edt,
max(edt) over(partition by brand order by stt rows between UNBOUNDED PRECEDING and 1 PRECEDING) maxedt
from test4;

        结果:

brand	 stt	        edt	     maxedt
huawei	2021-06-05	2021-06-26	NULL
huawei	2021-06-09	2021-06-15	2021-06-26
huawei	2021-06-17	2021-06-21	2021-06-26
oppo	2021-06-05	2021-06-09	NULL
oppo	2021-06-11	2021-06-21	2021-06-09
redmi	2021-06-05	2021-06-21	NULL
redmi	2021-06-09	2021-06-15	2021-06-21
redmi	2021-06-17	2021-06-26	2021-06-21
vivo	2021-06-05	2021-06-15	NULL
vivo	2021-06-09	2021-06-21	2021-06-15

        3)相减

                1、 第一个if判断maxedt是不是null,如果是null,那就当前行的(edt-stt)

                2、第二个if判断maxedt是不是比stt还小,如果小,那就不存在时间交叉的情况,那也直接(edt-stt) 就好了

                比如:

oppo	2021-06-05	2021-06-09	NULL
oppo	2021-06-11	2021-06-21	2021-06-09

                3、只有都不是上面两种情况下,才是(edt-maxedt+1)

datediff(edt,if(maxedt is null,stt,if(maxedt>stt,date_add(maxedt,1),stt))) days
select
    brand,stt,edt,
    datediff(edt,if(maxedt is null,stt,if(maxedt>stt,date_add(maxedt,1),stt))) days
from (
    select 
        brand,stt,edt,
        max(edt) over(partition by brand order by stt rows between UNBOUNDED PRECEDING and 1 PRECEDING) maxedt
    from test4
)t1;

        结果:

brand	stt	          edt	   days
huawei	2021-06-05	2021-06-26	21
huawei	2021-06-09	2021-06-15	-12
huawei	2021-06-17	2021-06-21	-6
oppo	2021-06-05	2021-06-09	4
oppo	2021-06-11	2021-06-21	10
redmi	2021-06-05	2021-06-21	16
redmi	2021-06-09	2021-06-15	-7
redmi	2021-06-17	2021-06-26	4
vivo	2021-06-05	2021-06-15	10
vivo	2021-06-09	2021-06-21	5

        4)按照品牌分组,计算每条数据加一的总和(加1和第3题相同)

select
brand,
sum(if(days>0,days+1,0)) days
from (
    select
        brand,stt,edt,
        datediff(edt,if(maxedt is null,stt,if(maxedt>stt,date_add(maxedt,1),stt))) days
    from (
        select 
            brand,stt,edt,
            max(edt) over(partition by brand order by stt rows between UNBOUNDED PRECEDING and 1 PRECEDING) maxedt
        from test4
    )t1
)t2
group by brand;

        结果:

brand	days
huawei	22
oppo	16
redmi	22
vivo	17

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值