Solving the Continuous Check-in Bonus Calculation Problem in SQL

Problem Overview

This problem involves calculating bonus coins earned from consecutive daily check-ins. The challenge lies in correctly identifying continuous check-in periods and applying bonus rules at specific intervals.

Table Structure

Table: tb_user_log

Core Concepts

Bonus Rules

  • Base: 1 coin per day
  • Every 3rd consecutive day: +2 bonus coins (total 5)
  • Every 7th consecutive day: +6 bonus coins (total 13)
  • Breaks reset the counter

Key Insight

The critical challenge is distinguishing between continuous and broken check-in sequences. The solution involves using date arithmetic combined with ranking to create session labels for each continuous check-in period.

When we subtract the row number from the actual check-in date, identical results indicate belonging to the same continuous session. When a break occurs, the date difference changes because the date shifts by at least one day while the row number resets.

Step-by-Step Solution

Step 1: Filter and Rank

First, filter the releavnt records and assign sequential row numbers within each user's check-in sequence:

SELECT 
    uid, 
    DATE(in_time) AS dt1,
    ROW_NUMBER() OVER (
        PARTITION BY uid 
        ORDER BY DATE(in_time)
    ) AS rn
FROM tb_user_log
WHERE DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31'
    AND article_id = 0
    AND sign_in = 1;

Step 2: Identify Sessions

Calculate the difference between the check-in date and row number to establish session boundaries:

SELECT 
    uid,
    dt1,
    DATE_SUB(dt1, INTERVAL rn DAY) AS session_key
FROM step1_result;

Step 3: Calculate Consecutive Day Numbers

Rank records within each session to determine which consecutive day each check-in represents:

SELECT 
    uid,
    dt1,
    ROW_NUMBER() OVER (
        PARTITION BY uid, session_key 
        ORDER BY dt1
    ) AS day_num
FROM step2_result;

Step 4: Apply Bonus Rules

Use modulo arithmetic to calculate coins based on the consecutive day number:

SELECT 
    uid,
    dt1,
    day_num,
    1 AS base_coins,
    CASE 
        WHEN day_num % 3 = 0 THEN 2 
        ELSE 0 
    END AS bonus_3,
    CASE 
        WHEN day_num % 7 = 0 THEN 6 
        ELSE 0 
    END AS bonus_7,
    (1 + 
        CASE WHEN day_num % 3 = 0 THEN 2 ELSE 0 END + 
        CASE WHEN day_num % 7 = 0 THEN 6 ELSE 0 END
    ) AS total_coins
FROM step3_result;

Step 5: Final Aggregation

Combine all steps using CTEs for better readability:

WITH filtered AS (
    SELECT 
        uid,
        DATE(in_time) AS dt1,
        ROW_NUMBER() OVER (PARTITION BY uid ORDER BY DATE(in_time)) AS rn
    FROM tb_user_log
    WHERE DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31'
        AND article_id = 0
        AND sign_in = 1
),
grouped AS (
    SELECT 
        uid,
        dt1,
        DATE_SUB(dt1, INTERVAL rn DAY) AS session_key
    FROM filtered
),
ranked AS (
    SELECT 
        uid,
        dt1,
        ROW_NUMBER() OVER (PARTITION BY uid, session_key ORDER BY dt1) AS day_num
    FROM grouped
),
calculated AS (
    SELECT 
        uid,
        dt1,
        day_num,
        1 + 
            CASE WHEN day_num % 3 = 0 THEN 2 ELSE 0 END +
            CASE WHEN day_num % 7 = 0 THEN 6 ELSE 0 END AS coins
    FROM ranked
)
SELECT 
    uid,
    dt1,
    day_num,
    coins
FROM calculated
ORDER BY uid, dt1;

Summary

The solution leverages window functions to create session identifiers through date arithmetic. By partitioning on both user ID and the computed session key, we can accurately track consecutive days within each uninterrupted check-in period. The modulo operator handles the periodic bonus multipliers elegantly.

Tags: sql Window Functions CTE continuous check-in problem row number

Posted on Tue, 22 Sep 2026 16:18:29 +0000 by something