0Pricing
Coding Interview Prep · Урок

Текущая активная серия на сегодня

Расчёт продолжающейся серии с её сбросом после перерыва

«Текущая активная серия на сегодня» — бесплатный урок Coding Interview Prep на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Coding Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Coding Interview Prep содержит 4 уроков всего.

Вопрос о продукте

Функциям серий (например, в Дуолинго или Снэпчате) нужна текущая серия, а не историческая самая длинная. Формулировка на собеседовании: "Сколько последовательных дней, заканчивающихся сегодня, каждый пользователь был активен? Если сегодня активности не было, сбросьте значение до 0."

Здесь появляется важный нюанс: серия должна быть привязана к сегодняшнему дню (или ко вчерашнему по правилу льготного периода). Построим её на основе метода разрывов и островов, который уже Вам знаком.

Определяем активность

Сначала уточните правила у интервьюера — эти решения изменят запрос:

  • Требует ли серия активности сегодня, или допустима вчера (льготный период)?
  • Объединяются ли несколько событий за день в один день?
  • Какой часовой пояс определяет "день"?

Будем считать, что на каждый активный день приходится одна строка и что серия считается текущей, если она включает сегодня OR вчера.

Снова строим острова

Снова используйте якорь: пронумеруйте дни каждого пользователя по дате и выполните вычитание. Каждая последовательная серия имеет общий якорь. Текущая серия — это просто остров, чей последний день приходится на сегодня или вчера.

WITH numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM logins
),
islands AS (
  SELECT user_id,
    MIN(login_date) AS streak_start,
    MAX(login_date) AS streak_end,
    COUNT(*)        AS streak_len
  FROM numbered
  GROUP BY user_id, login_date - rn
)
SELECT * FROM islands;

Находим последний остров

Самый недавний остров каждого пользователя — это остров с наибольшим значением streak_end. Проверим, достаточно ли близка эта дата к текущему моменту, чтобы считать серию продолжающейся.

CURRENT_DATE возвращает сегодняшнюю дату. CURRENT_DATE - 1 — вчерашний день. Если streak_end совпадает с одной из этих дат, серия продолжается.

WITH /* ...numbered, islands... */
latest AS (
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY streak_end DESC
    ) AS rn2
  FROM islands
)
SELECT * FROM latest WHERE rn2 = 1;

Проверяем актуальность

Теперь определите, активен ли последний остров. Если его конечная дата приходится на сегодня или вчера, текущая серия равна его длине; иначе пользователь прервал серию, и её текущее значение равно 0.

SELECT user_id,
  CASE
    WHEN streak_end >= CURRENT_DATE - 1
    THEN streak_len
    ELSE 0
  END AS current_streak
FROM latest
WHERE rn2 = 1;

Полный запрос для текущей серии

Объедините все слои: пронумеруйте строки, постройте острова, выберите последний остров для каждого пользователя, а затем примените CASE для проверки актуальности. Это полный ответ.

WITH numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM logins
),
islands AS (
  SELECT user_id,
    MAX(login_date) AS streak_end,
    COUNT(*)        AS streak_len
  FROM numbered
  GROUP BY user_id, login_date - rn
),
latest AS (
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY streak_end DESC
    ) AS rn2
  FROM islands
)
SELECT user_id,
  CASE WHEN streak_end >= CURRENT_DATE - 1
       THEN streak_len ELSE 0 END AS current_streak
FROM latest
WHERE rn2 = 1
ORDER BY user_id;

Строгий режим только для сегодняшнего дня

Если бизнес-правило строгое — серия считается активной только в том случае, если пользователь был активен сегодня, — измените сравнение, потребовав streak_end = CURRENT_DATE.

Вариант с льготным периодом (>= CURRENT_DATE - 1) более дружелюбен и часто используется в реальных приложениях, где день ещё не закончился. Всегда уточняйте, какой вариант нужен интервьюеру.

CASE WHEN streak_end = CURRENT_DATE
     THEN streak_len ELSE 0 END AS current_streak

Альтернатива: идём назад

Другая мысленная модель, которую предпочитают некоторые интервьюеры: вычислить разрыв от каждого дня до сегодняшнего дня. День является частью текущей серии, только если присутствует каждый день от него до сегодняшнего дня включительно. Иными словами, сравните login_date с CURRENT_DATE - (offset from the latest).

Метод островов обычно понятнее, но знание альтернативы показывает глубину понимания. Ключевая идея та же: непрерывная цепочка с якорем в сегодняшнем дне.

Включаем неактивных пользователей

Пользователи, отсутствующие в logins, не имеют острова и исчезают из результата. Если для каждого пользователя нужно вывести серию (скорее всего, 0), используйте LEFT JOIN со списком пользователей и замените результат на 0 с помощью COALESCE.

SELECT u.user_id,
  COALESCE(s.current_streak, 0) AS current_streak
FROM users u
LEFT JOIN current_streaks s ON s.user_id = u.user_id;

Подводные камни часовых поясов

Если активность хранится в виде временных меток, значение "сегодня" зависит от часового пояса. Выполните преобразование перед усечением до даты.

  • Храните данные и выполняйте сравнение в UTC либо сначала преобразуйте их в локальный часовой пояс пользователя.
  • В Postgres используйте event_ts AT TIME ZONE 'UTC' AT TIME ZONE user_tz, а затем преобразуйте результат к дате.
  • Наивное применение ::date к временной метке UTC может отнести вечернее событие не к тому календарному дню.

Если упомянуть это, можно заранее закрыть классический дополнительный вопрос.

SELECT user_id,
  (event_ts AT TIME ZONE 'UTC'
             AT TIME ZONE 'America/New_York')::date AS local_day
FROM events;

Производительность и завершение

Для панелей мониторинга, которые часто обновляются, этот запрос обрабатывает каждого пользователя за один проход. Чтобы он оставался быстрым:

  • Создайте индекс (user_id, login_date).
  • При необходимости ограничьте входные данные недавними датами — текущая серия не может включать данные старше собственной длины, поэтому скользящего окна, например, в 400 последних дней вполне достаточно.
  • Если чтение происходит очень часто, материализуйте ежедневные данные в таблицу серий.

Быстрая проверка

Убедитесь, что Вы понимаете привязку якоря к "текущему моменту".

Итоги

Чтобы вычислить текущую активную серию на сегодняшний день:

  • Постройте острова с помощью якоря login_date - ROW_NUMBER().
  • Выберите последний остров каждого пользователя по максимальному значению streak_end.
  • Верните его длину, только если streak_end равен сегодняшней дате (или вчерашней при льготном периоде), иначе верните 0.
  • Используйте LEFT JOIN с таблицей users для неактивных учётных записей; обработайте часовые пояса до усечения значения до даты.

Теперь Вы владеете полным набором инструментов анализа серий: поиск последовательностей, самой длинной серии, условий для N последовательных строк и текущей серии.

Часто задаваемые вопросы

Урок «Текущая активная серия на сегодня» бесплатный?

Да — полный текст урока «Текущая активная серия на сегодня» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Coding Interview Prep, подпишись на CoddyKit PRO. Курс Coding Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «Текущая активная серия на сегодня»?

Расчёт продолжающейся серии с её сбросом после перерыва Ты практикуешь Coding Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Coding Interview Prep?

Предыдущий опыт не требуется. Coding Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Текущая активная серия на сегодня»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Coding Interview Prep?

Да. Каждый урок Coding Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Поиск последовательных календарных дней
  2. Самая длинная серия для пользователя
  3. N последовательных строк, соответствующих условию
  4. Текущая активная серия на сегодня
← Назад к Coding Interview Prep