Раздел 42 · Аналитика данных с нуля

SQL-секция собеседования: лайвкодинг на время

middle~15 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Лада · руководитель агентства «Северный Тракт»

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

В уроке про карьеру мы разобрали, из каких секций состоит собеседование аналитика. Сегодня репетируем первую и самую предсказуемую: SQL. Её можно натренировать, потому что задачи на ней повторяются из компании в компанию. Ниже порядок решения, восемь классов задач, четыре ошибки под стрессом и три задачи на время.

Как проходит SQL-секция

Интервьюер присылает ссылку на общий редактор и описывает таблицы словами или схемой. Дальше 30-45 минут ты пишешь запросы, а он смотрит, как текст появляется на экране. Это и есть лайвкодинг.

Три особенности, которых нет в тестовом:

  • Запуска может не быть. Часто редактор только текстовый, и проверить запрос на базе нельзя. Значит, проверяешь в голове, на маленьком примере.
  • Времени мало. Обычно 2-3 задачи, от простой к сложной. Застрять на первой на 20 минут хуже, чем решить её грубо и вернуться.
  • Оценивают рассуждение. Интервьюер ставит оценку не за итоговый запрос, а за путь: что уточнил, какие ловушки назвал, как проверил. Молчаливое правильное решение ценится меньше, чем проговорённое решение с одной поправкой.

Поэтому говори вслух всё время. Не «сейчас подумаю» и тишина на минуту, а «у игрока может быть несколько сессий в день, значит дни сначала схлопну через DISTINCT».

Порядок решения: четыре шага

Разберём на задаче, которую любят давать продуктовым аналитикам. Интервьюер говорит: «Сколько игроков заходили в игру 7 дней подряд или больше?»

1. Уточни гранулярность и крайние случаи. Гранулярность отвечает на вопрос «что я считаю»: игроков, а не сессии и не серии. Дальше крайние случаи, по одному вопросу на каждый:

  • что такое день: по UTC или по местному времени? Интервьюер: по UTC, started_at::date;
  • если у игрока две сессии в один день, это один день серии? Да;
  • если у игрока две серии по 8 дней, он считается один раз? Да;
  • тестовые аккаунты считаем? Нет.

Каждый ответ сразу превращается в строчку запроса. Без этих вопросов запрос почти наверняка посчитает что-то соседнее.

2. Набросай CTE по шагам. Не пиши всё одним запросом. Каждый шаг это одна CTE с говорящим именем, как в уроке про CTE:

  1. days: одна строка на пару «игрок и день», тесты выкинуты;
  2. marked: метка серии, приём из урока про острова. Из дня вычитаем его номер по порядку, и у дней одной серии разность совпадает;
  3. series: длина каждой серии;
  4. итог: сколько разных игроков с серией от 7 дней.

SQL-песочница

База · guild

Загружаю редактор…

База загрузится, когда ты выполнишь запрос или откроешь таблицы.

Выполни запрос, чтобы увидеть результат.

Выполни весь запрос кнопкой или Ctrl/Cmd+Enter. Ctrl+Space открывает подсказки. Можно писать несколько команд через точку с запятой, менять данные и открывать транзакции. У каждой песочницы своя база, изменения живут до ухода со страницы. «Сбросить базу» возвращает учебные данные, но не меняет SQL.

Ответ: 207 игроков, самая длинная серия 20 дней.

3. Проверь на маленьком примере. Придумай три-четыре строки, на которых видна ловушка, и прогони их в голове или в песочнице. Вот игрок с двумя сессиями 1 июня, одной 2 июня и одной 4 июня, без DISTINCT:

WITH visits(day) AS (
  VALUES (date '2026-06-01'), (date '2026-06-01'), (date '2026-06-02'), (date '2026-06-04')
)
SELECT day, day - row_number() OVER (ORDER BY day)::int AS series
FROM visits;
    day     |   series
------------+------------
 2026-06-01 | 2026-05-31
 2026-06-01 | 2026-05-30
 2026-06-02 | 2026-05-30
 2026-06-04 | 2026-05-31

Серия 1-2 июня развалилась: второй сеанс 1 июня получил свой номер, и 4 июня попало в одну группу с 1 июня. С DISTINCT дни 1, 2 и 4 июня дают метки 31 мая, 31 мая и 1 июня: серия из двух дней и отдельный день, как и должно быть. На всей базе ошибка заметна: без DISTINCT выходит 397 игроков вместо 207. Скажи интервьюеру, что проверил этот случай.

4. Назови сложность. Одна-две фразы, без формул на доске. Здесь DISTINCT и окно сортируют строки: 38 113 сессий без тестовых аккаунтов схлопываются в 24 721 пару «игрок и день», окно сортирует их внутри каждого игрока. Это один проход с сортировкой, без коррелированных подзапросов, которые выполнялись бы для каждой строки. Если спросят, как ускорить, вспомни урок про индексы: индекс по (player_id, started_at) отдаёт сессии уже упорядоченными.

урок из курса

Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.