Skip to content
>_sqlbuddy

Longest Login Streak

For each user, the length of their longest run of consecutive login days.

Start practice

Problem statement

Prompt

Given the logins table below (one row per login, possibly several per user per day), write a query that returns, for each user, the length of their longest run of consecutive login days.

  • Output columns: user_id and longest_streak, ordered by user_id.
  • A user with a single login has a streak of 1.
  • Multiple logins on the same day count as one day.
  • If two runs are tied for the longest, the length is still reported once.

Example

user_idlogin_at
12024-01-01
12024-01-02
12024-01-04
22024-01-01

Expected result:

user_idlongest_streak
12
21

Constraints

  • logins has one row per login; a user can log in several times per day.
  • logins.login_at is a DATE and is not NULL.

Tips

  • Same islands technique as "Consecutive Login Days", but finish with MAX(streak_len) per user.
  • Dedupe to one row per (user, day) first, or the streak math silently breaks.

Schema

CREATE TABLE logins (
    id       INTEGER PRIMARY KEY,
    user_id  INTEGER NOT NULL,
    login_at DATE NOT NULL
);

Sample data

INSERT INTO logins (id, user_id, login_at) VALUES
  (1, 1, '2024-01-01'),
  (2, 1, '2024-01-02'),
  (3, 1, '2024-01-03'),   -- user 1: 3-day streak
  (4, 1, '2024-01-05'),
  (5, 2, '2024-01-01'),
  (6, 2, '2024-01-02'),   -- user 2: 2-day streak
  (7, 3, '2024-01-10');   -- user 3: 1 day

Additional hidden fixtures are applied during validation to test edge cases.

Solution

One correct approach — try solving it yourself in the practice editor first, then compare. There is usually more than one valid solution.

-- Reference: longest consecutive-day run per user.
WITH deduped AS (
    SELECT DISTINCT user_id, login_at
    FROM logins
),
numbered AS (
    SELECT user_id, login_at,
           ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_at) AS rn
    FROM deduped
),
islands AS (
    SELECT user_id,
           date(login_at, '-' || rn || ' days') AS grp,
           COUNT(*) AS streak_len
    FROM numbered
    GROUP BY user_id, date(login_at, '-' || rn || ' days')
)
SELECT user_id, MAX(streak_len) AS longest_streak
FROM islands
GROUP BY user_id
ORDER BY user_id;

Key concepts

  • Gaps and islands
  • ROW_NUMBER
  • MAX over groups
  • DISTINCT

Related questions

Learn more