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

连续签到领金币

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

# 连续签到,根据注的内容,应该只记进入时间,忽略离开时间,
# lead开窗函数,用户分页,新增一列是对应日期的下一个记录的日期
# 两列相减,如果是1,那就算连续;不是1,那就是不连续
# 从第一个1开始到第三个1,就是2, 到第七个就是6
# 日期:7.1 到 10.31 # 错了,是从7.7

# 原来直接date就能提取出日期

#以上是本来的想法

WITH temp_0 AS(
    SELECT DISTINCT uid, 
        DATE(in_time) date_now,  
        DENSE_RANK() OVER(PARTITION BY uid ORDER BY DATE(in_time)) rn
    FROM tb_user_log
    WHERE (artical_id = 0) AND (DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31') AND (sign_in = 1)
    ),
    temp_1 AS(
    SELECT uid, date_now, 
        CASE DENSE_RANK() OVER(PARTITION BY DATE_SUB(date_now, INTERVAL rn DAY), uid ORDER BY date_now) % 7
            WHEN 3 THEN 3
            WHEN 0 THEN 7
            ELSE 1 END cnt
    FROM temp_0
    )

SELECT uid, DATE_FORMAT(date_now, '%Y%m'), SUM(cnt)
FROM temp_1
GROUP BY uid, DATE_FORMAT(date_now, '%Y%m')
ORDER BY DATE_FORMAT(date_now, '%Y%m'), uid;

借鉴的第一个题解

梳理下思路,连续签到,根据注的内容,应该只记进入时间,忽略离开时间。日期:7.7 到 10.31

1.因此可以先查询必要信息,包括去重uid, in_time的日期,还有所有日期的排名(这里是为了后面计算日期的排名)

想想,如果是连续递增的,那么in_time的日期减去排名得到的日期都是一样的,由此得以判断是否连续。

第一次查询,就要根据题意把符合要求的记录提取出来,在where语句,日期、文章id,sign等,一定要注意日期是7.7开始,不是7.1.利用思维惯性埋坑了

2.临时表1,继续查询uid, in_time的日期, 根据所有日期的排名,用当天的日期减去排名数,使得连续的记录的计算得到的日期是相同的;再利用这个相同的日期和uid分页开窗,用当天的日期作为排序正排,得到了每个用户在同一周期的排序(周期是指1-7)。周期很重要,第一次查询得到的排名是每个用户的,没有限制周期,现在有了连续记录的判断依据,就能计算周期。

再将周期性排序的结果对7取余,如果余数是3,说明连续登录3天了,该多给2个金币,也就是3个;余数为0,就是连续登录7天了,同理给7个。这样求得了每个记录该给几个金币

3.主查询,把日期降维到年月,分组求和即可

重点是怎么知道又是一个新周期了,怎么确定3和7。那位大佬采用了排序的方式,通过排名的有序性和连续的日期相同解决了这个问题。

全部评论
其实想想可以使用一个lead和一个lag开窗函数,分别上推和下拉一个单位,然后和当前记录日期相减,遇到none的情况就分别用不是none的减当前的,这样也能区分出是否在一个周期里。局限在于没有考虑到不同周期的日期相减都落到同一个差里。还是那位大佬的思路清晰😭
点赞 回复 分享
发布于 2024-02-03 22:53 北京

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务