解法详析

题目:计算每个用户2021年7月以来每月获得的金币数(该活动到10月底结束,11月1日开始的签到不再获得金币),结果按月份、ID升序排序。
第一步,提取&加工数据,获得符合计算条件的数据
  • 提取数据的条件3个——签到:sign_in=1只有artical_id为0时sign_in值才有效:artical_id=0 & 活动期内:in_time between '2021-07-07 00:00:00' and '2021-11-01 00:00:00'
  • 计算所需的字段3个——uid,in_time,连续签到分段
将其存为临时表1,以简化代码,提升可读性,命名为tb_user_log_new。
<临时表1代码>:
SELECT distinct uid,date(in_time)in_date,(date(in_time)-row_number()over(partition by uid order by date(in_time)))row_index
FROM tb_user_log
WHERE artical_id=0 AND sign_in=1 AND year(in_time)=2021 AND (in_time between '2021-07-07 00:00:00' and '2021-11-01 00:00:00')
ORDER BY uid,in_date;
<临时表1输出结果>:

第二步,基于临时表1进一步加工,计算出每次连续签到所获得的金币
  • 对临时表1的连续签到分段再使用row_number()over()编序号,注意连续签到7天后重新累积这个规则,使用mod(序号,7)函数即取整除7的余数来实现,再次注意连续第7天签到时,余数为0;
  • 根据签到规则(即连续第3天签到得3个,连续第7天签到得7个,其余每天签到得1个)计算出每个用户每天获得的金币数。
将此步获得的数据结果存为临时表2,命名为tb_user_log_new_2。
<临时表2代码>:
WITH tb_user_log_new AS
(SELECT distinct uid,date(in_time)in_date,(date(in_time)-row_number()over(partition by uid order by date(in_time)))row_index
FROM tb_user_log
WHERE artical_id=0 AND sign_in=1 AND year(in_time)=2021 AND (in_time between '2021-07-07 00:00:00' and '2021-11-01 00:00:00')
ORDER BY uid,in_date)

SELECT uid,date_format(in_date,'%Y%m')month,(case mod(row_number()over(partition by uid,row_index order by in_date),7) when 3 then 3 when 0 then 7 else 1 end)coin
FROM tb_user_log_new;
<临时表2输出结果>:

第三步,对临时表3使用group by进行聚合,即可得到题目所需结果。完整代码详见参考代码。

参考代码

WITH tb_user_log_new AS
(SELECT distinct uid,date(in_time)in_date,(date(in_time)-row_number()over(partition by uid order by date(in_time)))row_index
FROM tb_user_log
WHERE artical_id=0 AND sign_in=1 AND year(in_time)=2021 AND (in_time between '2021-07-07 00:00:00' and '2021-11-01 00:00:00')
ORDER BY uid,in_date),
tb_user_log_new_2 AS
(SELECT uid,date_format(in_date,'%Y%m')month,(case mod(row_number()over(partition by uid,row_index order by in_date),7) when 3 then 3 when 0 then 7 else 1 end)coin
FROM tb_user_log_new)

SELECT uid,month,sum(coin)coin
FROM tb_user_log_new_2
GROUP BY uid,month
ORDER BY month,uid;
PS:共12行代码,结构化、可读性好、逻辑清晰,可能是目前为止本题最简洁的代码?欢迎PK~

运行结果