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

连续签到领金币

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

题目:

从2021年7月7日0点开始,每连续签到7天后重新累积签到天数。计算每个用户2021年7月以来每月获得的金币数(该活动到10月底结束,11月1日开始的签到不再获得金币)。结果按月份、ID升序排序


难点:

  • 连续签到问题,已经有很多题目考过了,不多赘述
  • 每7天重新计算累计签到天数 —— 这里有两种计算方式来解决:1)根据每一段连续签到,得出总累积天数,再一次性计算总金币数(我用的是后者);2)识别第3天、第7天,并赋予当天对应的金币数

方法 1:找出每个用户在每个月的每一段连续签到,再计算每一段对应的总金币数,最后再根据uid、月份来聚合

步骤 1:先找出每个用户在2021年7月7日 - 10月31日间,每一天的登录情况

SELECT DISTINCT uid, DATE(in_time) AS date,
                /* DENSE_RANK()用于后续匹配是否连续登录用 */
                DENSE_RANK() OVER (PARTITION BY uid ORDER BY DATE(in_time) ASC) AS rk
FROM tb_user_log
WHERE sign_in = 1 AND artical_id = 0 AND (DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31')

步骤 2:找出每个用户的每一段连续登录

SELECT uid, 
       /* 之所以是rk-1,是为了以连续登录的第一天为始点 */
       DATE_SUB(date, INTERVAL rk-1 DAY) AS start_date,
       COUNT(uid) AS days
FROM
    (SELECT DISTINCT uid, DATE(in_time) AS date,
                     DENSE_RANK() OVER (PARTITION BY uid ORDER BY DATE(in_time) ASC) AS rk
     FROM tb_user_log
     WHERE sign_in = 1 AND artical_id = 0 AND (DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31')) AS t1
GROUP BY uid, DATE_SUB(date, INTERVAL rk-1 DAY)

步骤 3:先根据每一段连续登录,计算其总金币数,再根据uid和月份聚合,得出每个用户在每个月的总金币数

  • 当连续天数 <= 2天,总金币 = 天数
  • 当连续天数 >= 3天,且 (连续天数 / 7) 的余数 <= 2,则总金币 = 天数 + 周数 X 2 + 周数 X 6
  • 当连续天数 >= 3天,且 (连续天数 / 7) 的余数 >= 3,则总金币 = 天数 + (周数+1) X 2 + 周数 X 6
  • 其中,周数 = 连续天数 / 7 所得的商的向下取整,即,FLOOR(连续天数 / 7)
    SELECT uid, DATE_FORMAT(start_date, '%Y%m') AS month,
           /* CASE WHEN用于计算每一段连续登录,应得的总金币数,最后结合SUM函数来聚合,
              得出每个用户在每个月的总金币数 */
           SUM(CASE WHEN days <= 2 THEN days
                WHEN days >= 3 AND MOD(days, 7) <= 2 THEN days + FLOOR(days / 7) * 2 + FLOOR(days / 7) * 6
                WHEN days >=3 AND MOD(days, 7) >= 3 THEN days + (FLOOR(days / 7) + 1) * 2 + FLOOR(days / 7) * 6
                ELSE NULL END) AS coin
    FROM
        (SELECT uid, DATE_SUB(date, INTERVAL rk-1 DAY) AS start_date, COUNT(uid) AS days
         FROM
             (SELECT DISTINCT uid, DATE(in_time) AS date,
                              DENSE_RANK() OVER (PARTITION BY uid ORDER BY DATE(in_time) ASC) AS rk
              FROM tb_user_log
              WHERE sign_in = 1 AND artical_id = 0 AND (DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31')) AS t1
         GROUP BY uid, DATE_SUB(date, INTERVAL rk-1 DAY)) AS t2
    GROUP BY uid, DATE_FORMAT(start_date, '%Y%m')
    ORDER BY month ASC, uid ASC;

方法 2:换个角度思考,当连续登录是第3天、第7天时,该两天可额外获得2金币、6金币,即,第3天和第7天,可以获得总共3金币和7金币,其余的天数皆是1金币

步骤 1:同样的,先找出每个用户在2021年7月7日 - 10月31日间,每一天的登录情况

SELECT DISTINCT uid, DATE(in_time) AS date,
                /* DENSE_RANK()用于后续匹配是否连续登录用 */
                DENSE_RANK() OVER (PARTITION BY uid ORDER BY DATE(in_time) ASC) AS rk
FROM tb_user_log
WHERE sign_in = 1 AND artical_id = 0 AND (DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31')

步骤 2:在找出每个用户的每一段连续登录后,根据uid,和连续登录的第一天来分组,对每一天登录再进行排序

  • 连续登录的第3天、第7天,可得到总共3和7金币
  • 对于其他连续登录的NO天数,即得1金币
    SELECT uid, DATE_SUB(date, INTERVAL rk-1 DAY) AS start_date, date,
           (CASE WHEN DENSE_RANK() OVER (PARTITION BY uid, DATE_SUB(date, INTERVAL rk-1 DAY) ORDER BY date ASC) % 7 = 3 THEN 3
                 WHEN DENSE_RANK() OVER (PARTITION BY uid, DATE_SUB(date, INTERVAL rk-1 DAY) ORDER BY date ASC) % 7 = 0 THEN 7
                 ELSE 1 END) AS coin
    FROM
        (SELECT DISTINCT uid, DATE(in_time) AS date,
                         DENSE_RANK() OVER (PARTITION BY uid ORDER BY DATE(in_time) ASC) AS rk
         FROM tb_user_log
         WHERE sign_in = 1 AND artical_id = 0 AND (DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31')) AS t1

步骤 3:根据uid和月份,使用SUM函数对所得金币进行聚合加总即可

SELECT uid, DATE_FORMAT(start_date, "%Y%m") AS month, SUM(coin) AS coins
FROM
    (SELECT uid, DATE_SUB(date, INTERVAL rk-1 DAY) AS start_date, date,
            (CASE WHEN DENSE_RANK() OVER (PARTITION BY uid, DATE_SUB(date, INTERVAL rk-1 DAY) ORDER BY date ASC) % 7 = 3 THEN 3
                WHEN DENSE_RANK() OVER (PARTITION BY uid, DATE_SUB(date, INTERVAL rk-1 DAY) ORDER BY date ASC) % 7 = 0 THEN 7
                ELSE 1 END) AS coin
     FROM
         (SELECT DISTINCT uid, DATE(in_time) AS date,
                          DENSE_RANK() OVER (PARTITION BY uid ORDER BY DATE(in_time) ASC) AS rk
          FROM tb_user_log
          WHERE sign_in = 1 AND artical_id = 0 AND (DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31')) AS t1) AS t2
GROUP BY uid, DATE_FORMAT(start_date, "%Y%m")
ORDER BY month ASC, uid ASC;
全部评论

相关推荐

kaoyu:腾讯基本都没hc了,只有零星捞人的
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务