问题:计算每个用户2021年7月以来每月获得的金币数(该活动到10月底结束,11月1日开始的签到不再获得金币)。结果按月份、ID升序排序。

注:如果签到记录的in_time-进入时间和out_time-离开时间跨天了,也只记作in_time对应的日期签到了

读题后思路如下:我们选出活动期间内所有签到的日期,判断其是连续签到的第几天,然后按月份和uid分组将所得硬币Sum即可。

1.这里有一个技巧。我们需要判断是否连续签到:则将用户签到的日期排序得到rk,用签到的日期减去rk 如果得到的这个差值日期相等则在连续签到。说起来有点抽象,但是只要举几个例子很快就能明白

select uid,dt,rk,date_sub(dt,interval rk day)  as rn from 
(select distinct uid,date(in_time) as dt,rank()over(partition by uid order by date(in_time)) as rk 
from tb_user_log
where artical_id=0 and sign_in=1) as u 
where dt between '2021-07-07' and '2021-10-3

我们使用上述代码即可将每个用户签到日期进行排序,然后用签到当天的日期减去这个排序号 得到rn 结果如下:

alt

仔细观察这个输出结果就能发现,我们这个原理是合理的。

2.我们接下来要做的就是求出所得硬币和,那么其实就是需要将同一用户同一rn的组按dt进行排序。这样就能得到他是连续登录的第几天了

select uid,dt,rn,rank()over(partition by uid,rn order by dt) as rm from p 

我们把一中的表作为p 即可得到如下结果:

alt

3.剩下的就是按月份以及用户进行分组然后求和了。注意题目中的规则,是连续签到的3、7天额外得2、6金币,那实际就是3,7金币。7天后进入第八天又开始算第一天,我们自然想到%除取余数即可。

select uid,date_format(dt,'%Y%m') as month,
sum(case when rm%7=0 then 7 when rm%7=3 then 3 else 1 end ) as coin 
from t
group by uid,month
order by month,uid

完整代码如下:

with t as 
(select uid,dt,rn,rank()over(partition by uid,rn order by dt) as rm from 
(select uid,dt,rk,date_sub(dt,interval rk day)  as rn from 
(select distinct uid,date(in_time) as dt,rank()over(partition by uid order by date(in_time)) as rk 
from tb_user_log
where artical_id=0 and sign_in=1) as u 
where dt between '2021-07-07' and '2021-10-31'
) as p )

select uid,date_format(dt,'%Y%m') as month,
sum(case when rm%7=0 then 7 when rm%7=3 then 3 else 1 end ) as coin 
from t
group by uid,month
order by month,uid