Classic islands pattern using row-number arithmetic.

Section: Gaps and islands

Group consecutive dates into streaks

sql
sql
WITH numbered AS (
  SELECT user_id,
         activity_date,
         ROW_NUMBER() OVER (
           PARTITION BY user_id
           ORDER BY activity_date
         ) AS rn
  FROM daily_activity
), grouped AS (
  SELECT user_id,
         activity_date,
         activity_date - rn * INTERVAL '1 day' AS grp_key
  FROM numbered
)
SELECT user_id,
       MIN(activity_date) AS streak_start,
       MAX(activity_date) AS streak_end,
       COUNT(*) AS streak_days
FROM grouped
GROUP BY user_id, grp_key;
Explanation

A timeless trick for finding consecutive runs. Date arithmetic varies slightly by dialect.

Learn the surrounding workflow

Compare similar commands or jump into common fixes when this command is part of a bigger troubleshooting path.

Related commands

Same sheet · prioritizing Gaps and islands
Flag a new session when the gap is too large
Start a new session after 30 minutes of inactivity.
OpenIn sheetsqlsame section
Assign session IDs with a running sum
Convert session boundary flags into stable groups.
OpenIn sheetsqlsame section
Deduplicate and keep the latest row
Choose one row per business key.
Delete duplicates after ranking
Keep the newest record and remove the rest.