Условие
Таблицы:
Accounts(id, name)Logins(id, login_date)—idссылается наAccounts.id. Возможны дубли (один пользователь — несколько входов в день).
Активный пользователь — тот, у кого есть серия из 5 и более подряд идущих дней с логином. Верните id, name, отсортируйте по id.
Решение
Идея — классический gaps & islands
Сводим логины к DISTINCT (id, login_date), вычитаем ROW_NUMBER() (умноженный на день) из даты — внутри одной серии разность константна.
WITH distinct_logins AS (
SELECT DISTINCT id, login_date
FROM Logins
),
grouped AS (
SELECT
id,
login_date,
login_date - ROW_NUMBER() OVER (PARTITION BY id ORDER BY login_date) * INTERVAL '1 day'
AS streak_key
FROM distinct_logins
),
streaks AS (
SELECT id, streak_key, COUNT(*) AS streak_len
FROM grouped
GROUP BY id, streak_key
)
SELECT DISTINCT a.id, a.name
FROM streaks s
JOIN Accounts a ON a.id = s.id
WHERE s.streak_len >= 5
ORDER BY a.id;Альтернатива через DATEDIFF между соседями
WITH d AS (
SELECT DISTINCT id, login_date FROM Logins
),
lagged AS (
SELECT
id, login_date,
SUM(CASE WHEN login_date = prev + 1 THEN 0 ELSE 1 END)
OVER (PARTITION BY id ORDER BY login_date) AS grp
FROM (
SELECT id, login_date,
LAG(login_date) OVER (PARTITION BY id ORDER BY login_date) AS prev
FROM d
) t
)
SELECT DISTINCT a.id, a.name
FROM (
SELECT id, grp, COUNT(*) AS len
FROM lagged GROUP BY id, grp
) s
JOIN Accounts a ON a.id = s.id
WHERE s.len >= 5
ORDER BY a.id;Подводные камни
- DISTINCT обязателен. Без него один день с двумя логинами разваливает шаг.
>=vs=5. Условие — «как минимум 5», а не «ровно 5».- Сортировка. Финальный
ORDER BY id— в задаче явно требуется.
Эталонный ответ
Gaps & islands. Distinct → ROW_NUMBER → date - rn*day → группа по streak_key → фильтр по COUNT(*) >= 5. Заработает в любой СУБД, только синтаксис интервалов разный.