Топ-20 SQL вопросов на собеседовании аналитика
SQL — главный навык на собеседовании аналитика данных. Неважно, куда вы идёте — в Яндекс, Тинькофф или стартап на 10 человек — без SQL не пройдёте техническую секцию. Мы собрали 20 вопросов, которые задают чаще всего, разбили по уровням и дали короткие ответы с примерами. Если хотите прорешать эти темы на практике — заходите в SQL-тренажёр.
Junior (вопросы 1–7)
1. Какие типы JOIN существуют и чем они отличаются?
Основные типы: INNER, LEFT, RIGHT, FULL и CROSS JOIN. INNER возвращает только совпавшие строки из обеих таблиц. LEFT — все строки из левой таблицы плюс совпадения из правой (если нет — NULL). FULL — все строки из обеих таблиц. CROSS — декартово произведение, каждая строка одной таблицы соединяется с каждой строкой другой.
Подробный разбор с примерами — в шпаргалке по JOIN.
2. В чём разница между WHERE и HAVING?
WHERE фильтрует строки до группировки, HAVING — после. WHERE не может использовать агрегатные функции, HAVING — может. Типичная ошибка — писать WHERE COUNT(*) > 5 вместо HAVING COUNT(*) > 5.
SELECT department_id, COUNT(*) AS cnt
FROM employees
WHERE status = 'active'
GROUP BY department_id
HAVING COUNT(*) > 5Подробнее: WHERE vs HAVING.
3. Чем COUNT(*) отличается от COUNT(column)?
COUNT(*) считает все строки, включая те, где есть NULL. COUNT(column) считает только строки, где значение в указанном столбце не NULL. Если в таблице 100 строк и в 20 из них поле email пустое, COUNT(*) вернёт 100, а COUNT(email) — 80.
4. Как NULL ведёт себя в сравнениях и агрегатах?
NULL — это не значение, а отсутствие значения. Любое сравнение с NULL (включая NULL = NULL) возвращает NULL, а не TRUE. Для проверки используйте IS NULL / IS NOT NULL. Агрегатные функции (SUM, AVG, COUNT(column)) игнорируют NULL. Подробнее про агрегаты — в шпаргалке по агрегатным функциям.
5. Как выбрать топ-N записей?
Используйте ORDER BY + LIMIT (в PostgreSQL, MySQL) или TOP (в SQL Server). Частый вопрос на собеседовании — «выведите 3 самых дорогих заказа»:
SELECT *
FROM orders
ORDER BY amount DESC
LIMIT 36. Что делает DISTINCT и когда его не стоит использовать?
DISTINCT убирает дубликаты из результата. Полезен, когда нужно получить уникальные значения: SELECT DISTINCT city FROM users. Но если вы используете DISTINCT, чтобы «починить» результат после неправильного JOIN — это сигнал, что JOIN написан некорректно. DISTINCT не лечит ошибки в логике запроса.
7. Как работают базовые агрегатные функции?
SUM, AVG, MIN, MAX, COUNT — пять основных агрегатных функций. Все они работают в паре с GROUP BY. Без GROUP BY агрегат применяется ко всей таблице целиком. SUM и AVG игнорируют NULL. MIN и MAX работают не только с числами, но и с датами и строками.
Middle (вопросы 8–14)
8. Что такое оконные функции и чем они отличаются от GROUP BY?
Оконные функции выполняют вычисления по группам строк, не сворачивая результат. GROUP BY превращает 100 строк в 5, оконная функция оставляет все 100 и добавляет к каждой строке вычисленное значение. Классический пример — пронумеровать заказы каждого пользователя по дате через ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at).
Полный разбор: оконные функции SQL.
9. В чём разница между ROW_NUMBER, RANK и DENSE_RANK?
ROW_NUMBER — уникальный номер каждой строке, дубликатов нет. RANK — одинаковые значения получают одинаковый ранг, следующий номер пропускается (1, 1, 3). DENSE_RANK — то же, но без пропуска (1, 1, 2). На собеседовании часто просят найти «второй по величине» — и здесь важно выбрать правильную функцию.
10. Что такое CTE и зачем он нужен?
CTE (Common Table Expression) — именованный временный результат запроса, определённый через WITH. CTE делает сложный запрос читаемым: вместо вложенных подзапросов на 3 уровня вы разбиваете логику на именованные блоки. CTE существует только в рамках одного запроса и не создаёт таблицу на диске.
WITH active_users AS (
SELECT user_id, COUNT(*) AS sessions
FROM events
WHERE event_date >= '2026-03-01'
GROUP BY user_id
)
SELECT * FROM active_users WHERE sessions > 10Подробнее: CTE vs подзапросы.
11. Как использовать CASE WHEN?
CASE WHEN — условная логика внутри SQL. Позволяет создавать новые колонки на основе условий, группировать значения в категории, подставлять значения по умолчанию. На собеседовании часто просят сегментировать пользователей:
SELECT
user_id,
CASE
WHEN total_spent > 10000 THEN 'VIP'
WHEN total_spent > 1000 THEN 'Regular'
ELSE 'New'
END AS segment
FROM usersДетальный разбор: CASE WHEN в SQL.
12. Какие функции для работы с датами нужно знать?
На собеседовании чаще всего спрашивают: DATE_TRUNC (округление даты до месяца/недели), DATE_DIFF / AGE (разница между датами), EXTRACT (извлечение года, месяца, дня), CURRENT_DATE. Типичная задача — «посчитайте количество регистраций по месяцам»:
SELECT
DATE_TRUNC('month', created_at) AS month,
COUNT(*) AS registrations
FROM users
GROUP BY DATE_TRUNC('month', created_at)13. Когда использовать подзапрос, а когда JOIN?
Подзапрос в WHERE удобен для фильтрации: «покажите пользователей, которые сделали хотя бы один заказ». JOIN лучше, когда нужно вытащить данные из связанной таблицы. Коррелированный подзапрос выполняется для каждой строки — на больших таблицах это медленно. CTE или JOIN обычно эффективнее. Подробнее: подзапросы в SQL.
14. В каком порядке SQL выполняет запрос?
Порядок написания (SELECT → FROM → WHERE) не совпадает с порядком выполнения. Реальный порядок: FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT. Поэтому нельзя использовать алиас из SELECT в WHERE, но можно в ORDER BY. Полный разбор: порядок выполнения SQL-запроса.
Senior (вопросы 15–20)
15. Как оптимизировать медленный SQL-запрос?
Начните с EXPLAIN (ANALYZE) — он покажет план выполнения и узкие места. Типичные проблемы: полный перебор таблицы (Seq Scan) вместо использования индекса, неоптимальный JOIN-порядок, лишние подзапросы. Основные приёмы: добавить индексы на колонки в WHERE и JOIN, заменить коррелированные подзапросы на JOIN, ограничить выборку через WHERE до JOIN.
16. Что такое индексы и когда их создавать?
Индекс — структура данных, ускоряющая поиск по таблице (как оглавление в книге). B-tree индекс — самый распространённый, хорошо работает для точных совпадений и диапазонов. Создавайте индексы на колонки, по которым часто фильтруете (WHERE), соединяете (JOIN ON) и сортируете (ORDER BY). Но индексы замедляют INSERT/UPDATE, поэтому не индексируйте всё подряд. Подробнее: индексы в SQL.
17. В чём разница между EXISTS и IN, особенно с NULL?
EXISTS проверяет, возвращает ли подзапрос хотя бы одну строку. IN сравнивает значение со списком. Критичная разница: если в подзапросе IN есть NULL, результат NOT IN сломается — вернёт пустой набор. NOT EXISTS работает корректно с NULL. На больших данных EXISTS обычно быстрее, потому что останавливается на первом совпадении.
-- Безопасно с NULL
SELECT * FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
)Детали: EXISTS vs IN.
18. Напишите SQL для расчёта retention
Классическая задача на senior-собеседовании. Retention Day 7 — доля пользователей, вернувшихся на 7-й день после регистрации. Нужен self-join или оконные функции:
WITH first_visit AS (
SELECT user_id, MIN(event_date) AS reg_date
FROM events
GROUP BY user_id
)
SELECT
f.reg_date,
COUNT(DISTINCT e.user_id)::float / COUNT(DISTINCT f.user_id) AS retention_d7
FROM first_visit f
LEFT JOIN events e
ON f.user_id = e.user_id
AND e.event_date = f.reg_date + INTERVAL '7 days'
GROUP BY f.reg_date19. Напишите SQL для воронки конверсии
Нужно посчитать, сколько пользователей прошло каждый шаг: регистрация → просмотр товара → добавление в корзину → покупка. Используйте COUNT(DISTINCT) с условиями или несколько CTE:
SELECT
COUNT(DISTINCT CASE WHEN event = 'registration' THEN user_id END) AS step_1,
COUNT(DISTINCT CASE WHEN event = 'view_product' THEN user_id END) AS step_2,
COUNT(DISTINCT CASE WHEN event = 'add_to_cart' THEN user_id END) AS step_3,
COUNT(DISTINCT CASE WHEN event = 'purchase' THEN user_id END) AS step_4
FROM events
WHERE event_date BETWEEN '2026-03-01' AND '2026-03-31'20. Как читать EXPLAIN и что такое Seq Scan, Index Scan, Hash Join?
EXPLAIN показывает план выполнения запроса. Seq Scan — последовательный перебор всей таблицы (медленно на больших таблицах). Index Scan — поиск через индекс (быстро). Hash Join — соединение через хэш-таблицу, эффективно для больших таблиц. Nested Loop — вложенный цикл, эффективен когда одна из таблиц маленькая. EXPLAIN ANALYZE выполняет запрос и показывает реальное время — используйте его для диагностики.
Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.
FAQ
Сколько SQL нужно знать для собеседования на junior-аналитика?
Достаточно уверенно владеть SELECT, WHERE, GROUP BY, HAVING, JOIN (INNER + LEFT), ORDER BY, LIMIT и базовыми агрегатами. Это покрывает 80% задач на junior-позицию. Для практики используйте примеры вопросов из реальных собеседований.
Какие SQL-темы спрашивают чаще всего?
По нашей статистике из 1500+ вопросов: JOIN (особенно LEFT vs INNER), оконные функции (ROW_NUMBER, LAG), GROUP BY + HAVING, подзапросы и работа с NULL. На middle+ добавляются оптимизация, индексы и аналитические задачи (retention, воронки, когорты).
Как готовиться к SQL-части собеседования?
Решайте задачи руками — не читайте, а пишите код. Начните с базовых конструкций и постепенно переходите к оконным функциям и аналитическим запросам. Заходите в SQL-тренажёр Карьерника — там 500+ задач по SQL, сгруппированных по темам и уровням сложности.
Нужно ли учить конкретный диалект SQL (PostgreSQL, MySQL, ClickHouse)?
На собеседовании обычно принимают любой диалект, главное — понимание логики. Но PostgreSQL — самый популярный выбор среди аналитиков: в нём есть оконные функции, CTE, DATE_TRUNC и массивы. Если сомневаетесь — учите PostgreSQL.