Identifying Users with Consecutive Login Streaks Using SQL

Detecting uninterrupted activity patterns in event logs is a classic relational database challenge often referred to as the gaps-and-islands problem. This guide outlines two efficient SQL strategies to extract accounts that have accessed a platform for five consecutive days, leveraging MySQL 8.0+ capabilities.

Environment & Data Preparation

Begin by defining a schema to track access events. The following DDL creates a table to store client identifiers and their corresponding login dates, followed by a dataset that includes valid five-day streaks and fragmented login patterns for testing.

CREATE TABLE app_login_history (
    log_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    client_id VARCHAR(50),
    access_timestamp DATE
);

INSERT INTO app_login_history (client_id, access_timestamp) VALUES
('client_alpha', '2023-08-01'), ('client_alpha', '2023-08-02'), ('client_alpha', '2023-08-03'),
('client_alpha', '2023-08-04'), ('client_alpha', '2023-08-05'),
('client_beta',  '2023-08-01'), ('client_beta',  '2023-08-03'), ('client_beta',  '2023-08-04'),
('client_gamma', '2023-08-01'), ('client_gamma', '2023-08-02'), ('client_gamma', '2023-08-03'),
('client_gamma', '2023-08-04'), ('client_gamma', '2023-08-05'), ('client_gamma', '2023-08-06');

Method 1: Window Function Approach (User Identification Only)

This technique relies on sequential numbering to mathematically isolate consecutive date blocks. By partitioning records by client and assiginng a row number ordered chronologically, subtracting the sequence value from the actual date produces a constant offset to consecutive days. Grouping by this offset reveals uninterrupted streaks without pinpointing the exact start date.

WITH cleaned_logs AS (
    SELECT client_id, access_timestamp
    FROM app_login_history
    GROUP BY client_id, access_timestamp
),
sequenced_records AS (
    SELECT 
        client_id,
        access_timestamp,
        ROW_NUMBER() OVER(PARTITION BY client_id ORDER BY access_timestamp) AS seq_val
    FROM cleaned_logs
)
SELECT DISTINCT client_id
FROM sequenced_records
GROUP BY client_id, DATE_SUB(access_timestamp, INTERVAL seq_val DAY)
HAVING COUNT(*) >= 5;

Method 2: Self-Join Technique (Identifying Streak Start Dates)

When business requirements demand the exact origin date of each consecutive period, a temporal self-join proves effective. The deduplicated access table is joined against itself using a range condition: every login date attempts to match subsequent dates falling within a five-day window. Aggregating these matches and filtering for a count of five or more isolates the precise starting timestamp of each valid streak.

WITH distinct_access AS (
    SELECT client_id, access_timestamp
    FROM app_login_history
    GROUP BY client_id, access_timestamp
)
SELECT 
    start_day.client_id,
    start_day.access_timestamp AS streak_origin
FROM distinct_access start_day
INNER JOIN distinct_access match_day
  ON start_day.client_id = match_day.client_id
  AND match_day.access_timestamp BETWEEN start_day.access_timestamp 
  AND DATE_ADD(start_day.access_timestamp, INTERVAL 4 DAY)
GROUP BY start_day.client_id, start_day.access_timestamp
HAVING COUNT(DISTINCT match_day.access_timestamp) >= 5;

Tags: sql MySQL Window-Functions gaps-and-islands date-arithmetic

Posted on Sat, 24 Oct 2026 16:23:09 +0000 by ralph4100