Consecutive Login Days
Find users who logged in on three or more consecutive calendar days.
Start practiceProblem statement
Prompt
Given the logins table below (one row per login, possibly several per user per day), write a query that returns every user who logged in on 3 or more consecutive calendar days.
- Each qualifying user appears once, with a
consecutive_dayscolumn showing the length of that consecutive run. - If a user has two separate runs of 3+ days, they appear once per run.
- Multiple logins on the same day count as one day for streak purposes.
- Only
user_idandconsecutive_daysshould be returned.
Example
| user_id | login_at |
|---|---|
| 1 | 2024-01-01 |
| 1 | 2024-01-02 |
| 1 | 2024-01-03 |
| 2 | 2024-01-01 |
| 2 | 2024-01-03 |
Expected result:
| user_id | consecutive_days |
|---|---|
| 1 | 3 |
Constraints
loginshas one row per login; a user can log in several times per day.logins.login_atis aDATEand is notNULL.
Tips
- Dedupe to one row per (user, day), then use the gaps and islands trick:
login_at - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_at)is constant for consecutive days. - In SQLite, subtract the row number with
julianday(login_at) - rnordate(login_at, '-' || rn || ' days'). - Group by (user, island key) and keep
HAVING COUNT(*) >= 3.
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'),
(4, 2, '2024-01-01'),
(5, 2, '2024-01-03'),
(6, 2, '2024-01-04'),
(7, 3, '2024-01-01'),
(8, 3, '2024-01-02'),
(9, 3, '2024-01-04');
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: users with a run of 3+ consecutive login days (islands technique).
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
FROM numbered
)
SELECT user_id, COUNT(*) AS consecutive_days
FROM islands
GROUP BY user_id, grp
HAVING COUNT(*) >= 3
ORDER BY user_id, grp;
Key concepts
- ROW_NUMBER
- Gaps and islands
- CTE
- julianday
- DISTINCT
Related questions
- mediumConsecutive NumbersFind numbers that appear three or more times in a row.
- mediumFirst and Last Order Per CustomerShow the date of each customer's first and last order.
- mediumGame Play Analysis IVFind the fraction of players who logged in the day after their first login.
- mediumLatest Event Per UserReturn each user's most recent event in full.
- mediumLongest Login StreakFor each user, the length of their longest run of consecutive login days.