Вопросы по SQL на собеседовании аналитика
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
SQL — ядро большинства собесов аналитика. Без прохода SQL-секции дальше не пустят. Подготовиться к частым вопросам = значительно поднять шансы.
Ниже — топ-30 вопросов с коротким разбором. Если видите каждый впервые — значит вы junior. Если ответите на все без подготовки — уверенный middle.
Часть 1: базовые
1. Разница WHERE и HAVING
- WHERE: фильтрует до группировки (строки)
- HAVING: фильтрует после группировки (агрегаты)
2. Разница JOIN'ов
- INNER: только совпадения
- LEFT: все из левой + совпавшие
- RIGHT: наоборот (редко)
- FULL OUTER: все из обеих
- CROSS: декартово произведение
3. Что быстрее: COUNT(*) или COUNT(1)?
Одинаково в современных СУБД. COUNT(*) — стандарт.
4. COUNT(*) vs COUNT(column)?
COUNT(*) включает NULL. COUNT(column) — только non-NULL.
5. DISTINCT vs GROUP BY?
Функционально часто эквивалентны. GROUP BY удобнее при агрегациях.
6. Порядок выполнения SQL
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT.
7. Что такое NULL?
«Неизвестное» значение. NULL = NULL → UNKNOWN (не TRUE).
8. Как работает NULL в сравнении?
col = NULL всегда FALSE/UNKNOWN. Используйте IS NULL.
9. UNION vs UNION ALL
- UNION: удаляет дубликаты (медленнее)
- UNION ALL: оставляет (быстрее)
10. Что такое индекс?
Структура для быстрого поиска. B-tree на колонке → O(log N) instead of O(N).
Часть 2: средний уровень
11. Оконные функции
Считают над «окном» строк без свёртки. ROW_NUMBER, RANK, SUM OVER, LAG.
12. ROW_NUMBER vs RANK vs DENSE_RANK
- ROW_NUMBER: уникальные номера
- RANK: повторы при связке, пропуски следующих
- DENSE_RANK: повторы без пропусков
13. PARTITION BY vs GROUP BY
PARTITION BY — в оконной функции, не сворачивает строки. GROUP BY — сворачивает.
14. CTE (WITH)
Именованный подзапрос для читаемости и переиспользования.
15. Когда CTE vs подзапрос?
CTE — для читаемости, рекурсии, переиспользования. Подзапрос — для одноразового.
16. Топ-N в каждой группе
SELECT * FROM (
SELECT *, DENSE_RANK() OVER (PARTITION BY cat ORDER BY val DESC) AS rnk
FROM t
) WHERE rnk <= 3;17. Вторая максимальная зарплата
SELECT MAX(salary) FROM employees WHERE salary < (SELECT MAX(salary) FROM employees);Или DENSE_RANK = 2.
18. Дубликаты по email
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;19. Running total
SELECT day, SUM(rev) OVER (ORDER BY day) FROM daily;20. LAG для DoD
SELECT day, rev, rev - LAG(rev) OVER (ORDER BY day) AS dod FROM daily;Часть 3: продвинутый
21. EXISTS vs IN
EXISTS — коррелированный, safer с NULL. IN — простой список.
22. NOT IN с NULL
Если в подзапросе есть NULL → результат пустой. Используйте NOT EXISTS.
23. Median в SQL
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY col) FROM t;24. PIVOT в SQL
Через CASE WHEN + GROUP BY. Нативный PIVOT только в MSSQL / BigQuery.
25. Рекурсивный CTE
WITH RECURSIVE t AS (
SELECT ... -- base CASE
UNION ALL
SELECT ... -- RECURSIVE CASE
)Для hierarchies.
26. Оптимизация медленного запроса
EXPLAIN → добавить индекс → избавиться от function(col) в WHERE → переписать с JOIN вместо подзапросов.
27. EXPLAIN plan
Seq Scan — без индекса. Index Scan — с индексом. Cost = estimated time.
28. Когда денормализация
OLAP (DWH, star schema) — denormalize ради скорости queries. OLTP — normalize (3NF).
29. ACID
Atomicity, Consistency, Isolation, Durability — свойства транзакций.
30. Как посчитать retention D7
WITH cohort AS (
SELECT user_id, MIN(DATE(event_at)) AS d0 FROM events GROUP BY user_id
)
SELECT
COUNT(DISTINCT CASE WHEN DATE(e.event_at) = c.d0 + 7 THEN e.user_id END) /
COUNT(DISTINCT c.user_id) AS d7_retention
FROM cohort c LEFT JOIN events e USING (user_id);Кейсы (часто дают задачу)
Найти пользователей без покупок
SELECT u.* FROM users u
LEFT JOIN orders o USING (user_id)
WHERE o.order_id IS NULL;Conversion воронки
SELECT
COUNT(DISTINCT CASE WHEN event = 'signup' THEN user_id END) AS s,
COUNT(DISTINCT CASE WHEN event = 'active' THEN user_id END) AS a,
COUNT(DISTINCT CASE WHEN event = 'paid' THEN user_id END) AS p
FROM events;Топ-5 клиентов по выручке
SELECT user_id, SUM(total) AS rev FROM orders GROUP BY 1 ORDER BY rev DESC LIMIT 5;На собесе
Тренируйтесь на реальных задачах. В Карьернике — 1500+ вопросов с собесов топ-компаний.
Связанные темы
FAQ
Все ли 30 спросят?
Нет, 10-15. Готовьтесь ко всем.
Junior уровень?
1-20. Senior — все.
Что критичнее: синтаксис или идея?
Идея. Синтаксис можно чуть запутать, но если поняли задачу — ок.
Онлайн-IDE на собесе?
Иногда. Чаще на whiteboard — пишите вручную.
Готовьтесь к собесу — откройте тренажёр с 1500+ вопросами для аналитиков.