题解 | #连续签到领金币#

连续签到领金币

http://www.nowcoder.com/practice/aef5adcef574468c82659e8911bb297f

本题有2个难点:
  1. 解决经典问题 -- 连续签到
  2. 签到金币数量如何转化成代码语言

【难点1】连续签到天数

这是一个经典问题!面试中常会出现!必须熟练!
解题思路:
1、筛选出用户连续签到的dt
2、使用row_number()窗口函数按照dt升序进行排序
  • row_number 排序结果是 1、2、3、4……
  • rank 排序结果是 1、2、2、4……
  • dense_rank 排序结果是 1、2、2、3……
3、如果签到日期是连续的,则签到日期-排序得到的日期相同。由此按照得到的日期进行分组,则能计算出用户各连续签到天数

【难点2】计算签到当日领取金币的数量

先来看领取金币数量规则
  • 用户每天签到可以领1金币,并可以开始累积签到天数,连续签到的第3、7天分别可额外领2、6金币。
  • 每连续签到7天后重新累积签到天数
这里,按照连续签到天数7的余数来处理即可
  • 当签到天数%7=3 则领取3金币
  • 当签到天数%7=0 则领取7金币
  • 其余情况,领取1金币
突破以上2个难点后,就可以开始解题了~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

首先,筛选出各用户符合条件的签到日期,得到<签到表>。条件需要满足
  • 签到日期为2021-07-07 0时至2021-10-31 24时
  • artical_id=0
  • and sign_in=1
  • 签到只以in_time对应的日期为准
SELECT * FROM tb_user_log
where date(in_time) between '2021-07-07' and '2021-10-31'
and artical_id=0
and sign_in=1;
其次,对签到表进行排序。使用窗口函数row_number()
  • 以uid分组
  • 按照date(in_time)排序
SELECT DISTINCT uid,date(in_time) dt,
row_number() over (partition by uid order by date(in_time)) ranking 
FROM tb_user_log
where date(in_time) between '2021-07-07' and '2021-10-31'
and artical_id=0
and sign_in=1;
然后,求出各用户连续登录的天数。用dt-ranking,得到日期相同的为一组。此组数量则为连续签到天数
SELECT DISTINCT uid,date(in_time) dt,
row_number() over (partition by uid order by date(in_time)) ranking,
date(in_time)-(row_number() over (partition by uid order by date(in_time))) dt2 
FROM tb_user_log
where date(in_time) between '2021-07-07' and '2021-10-31'
and artical_id=0
and sign_in=1;
再来计算日期对应的连续签到天数

select *,row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt) ranking2 FROM
(SELECT DISTINCT uid,date(in_time) dt,
row_number() over (partition by uid order by date(in_time)) ranking,
date(in_time)-(row_number() over (partition by uid order by date(in_time))) dt2
FROM tb_user_log
where date(in_time) between '2021-07-07' and '2021-10-31'
and artical_id=0
and sign_in=1)t1
;
有了连续签到天数ranking2,就可以攻克每日领取的金币数量了,取7的余数

select *,row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt) ranking2,
case 
when row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt)%7=3 then 3
when row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt)%7=0 then 7
else 1 end coin 
FROM
(SELECT DISTINCT uid,date(in_time) dt,
row_number() over (partition by uid order by date(in_time)) ranking,
date(in_time)-(row_number() over (partition by uid order by date(in_time))) dt2
FROM tb_user_log
where date(in_time) between '2021-07-07' and '2021-10-31'
and artical_id=0
and sign_in=1)t1
;

好了,按照uid,月份分组,求出对应的coin和。并且按照月份、ID升序排序

select t2.uid,DATE_FORMAT(t2.dt,'%Y%m') month,sum(t2.coin) coin FROM
(select *,row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt) ranking2,
case 
when row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt)%7=3 then 3
when row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt)%7=0 then 7
else 1 end coin 
FROM
(SELECT DISTINCT uid,date(in_time) dt,
row_number() over (partition by uid order by date(in_time)) ranking,
date(in_time)-(row_number() over (partition by uid order by date(in_time))) dt2
FROM tb_user_log
where date(in_time) between '2021-07-07' and '2021-10-31'
and artical_id=0
and sign_in=1)t1
)t2
group by t2.uid,DATE_FORMAT(t2.dt,'%Y%m')
order by t2.uid,DATE_FORMAT(t2.dt,'%Y%m')
;




全部评论
想问一下,这里面不会存在某个用户一天登录两次么 这样的话在对签到表前行排序row number()时不会存在一个用户的某一天对应了两个数字ranking么
1 回复 分享
发布于 2022-03-05 14:58
用dt-ranking的想法太牛了
1 回复 分享
发布于 2022-03-28 22:31
case row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt)%7 when 3 then 3 when 0 then 7 else 1 end coin
1 回复 分享
发布于 2022-12-20 13:58 北京
ranking2,看着没啥用呀
1 回复 分享
发布于 2023-08-17 11:14 北京
这个真的太详细了,条理清晰,看了这么多个,终于看懂了
点赞 回复 分享
发布于 2022-02-11 14:36
清晰明了,太赞了!
点赞 回复 分享
发布于 2022-02-19 11:08
这个好,very good!
点赞 回复 分享
发布于 2022-02-24 14:25
确认了!宝藏楼主
点赞 回复 分享
发布于 2022-03-08 20:54
必须高赞,逻辑好清晰
点赞 回复 分享
发布于 2022-03-15 15:40
非常清晰,谢谢!
点赞 回复 分享
发布于 2022-07-29 16:39
题目没有很明确隔月(甚至隔年)的连签要怎么算积分,测试用例的数据也是没有隔月连签,我照着你思路做了点修改,把你dt换成了dayofyear(in_time),算出当年的第几天,减去按用户uid分组的row_number(),就会得到当年连签的判断标识sign_num。如果想处理隔月的连签,在tmp2表里分组时在uid后删除sign_month就可以。
点赞 回复 分享
发布于 2023-03-05 18:59 广东
非常出色的解答 清晰又完善
点赞 回复 分享
发布于 2023-05-17 16:08 北京
用户102的ranking2是断了的,但row_number之后的排序还是会连续起来呀,那为啥只row_number,就可以得到是否连续,从而算出金币呢?
点赞 回复 分享
发布于 2023-08-17 10:58 北京
想问一下为什么要distinct
点赞 回复 分享
发布于 02-21 11:01 重庆

相关推荐

吃不饱的肱二头肌很想退休:tnnd 我以为选妹子呢,亏我兴高采烈的冲进来😠
投递快手等公司10个岗位
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-20 19:57
已编辑
某大厂 golang工程师 23.0k*16.0, 2k房补,年终大概率能拿到
点赞 评论 收藏
分享
117 17 评论
分享
牛客网
牛客企业服务