Собрали вопросы, которые чаще всего задают на собеседованиях аналитикам данных, backend-разработчикам и Data Engineers. Для каждого — короткий, точный ответ.
Основы
1. Чем отличается WHERE от HAVING?
WHERE фильтрует строки до группировки.
HAVING фильтрует группы после GROUP BY.
SELECT city, COUNT(*) AS cnt
FROM orders
WHERE status = 'paid' -- до группировки
GROUP BY city
HAVING COUNT(*) > 10; -- после группировки
2. Что такое NULL и как с ним работать?
NULL — отсутствие значения. Любое сравнение с NULL возвращает NULL (не TRUE/FALSE). Проверять нужно через IS NULL / IS NOT NULL.
WHERE phone IS NULL -- ✅
WHERE phone = NULL -- ❌ всегда вернёт 0 строк
3. В чём разница между DELETE, TRUNCATE и DROP?
| Команда | Что делает | Откат (ROLLBACK) |
|---|---|---|
DELETE | Удаляет строки с условием | Да |
TRUNCATE | Удаляет все строки, быстро, сбрасывает счётчики SEQUENCE | В большинстве БД — нет |
DROP | Удаляет таблицу целиком | Нет |
4. Что такое первичный ключ (PRIMARY KEY)?
Уникальный идентификатор строки. Не может быть NULL. На него автоматически создаётся индекс. Таблица может иметь только один PRIMARY KEY (но составной — из нескольких столбцов).
JOIN
5. Какие виды JOIN существуют?
INNER JOIN— строки, которые есть в обеих таблицахLEFT JOIN— все строки из левой + совпадения из правой (NULL если нет)RIGHT JOIN— все из правой + совпадения из левойFULL JOIN— все строки из обеих таблицCROSS JOIN— декартово произведение (каждая строка × каждую)
6. Что вернёт LEFT JOIN если в правой таблице нет совпадений?
NULL для всех столбцов правой таблицы.
SELECT u.name, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- Пользователи без заказов: name = 'Иван', amount = NULL
7. Как найти строки, которые есть в одной таблице, но нет в другой?
-- Через LEFT JOIN
SELECT u.id FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.user_id IS NULL;
-- Через NOT EXISTS (часто быстрее)
SELECT id FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
Агрегация и группировка
8. Почему нельзя использовать агрегатные функции в WHERE?
WHERE выполняется до агрегации — значений SUM(), COUNT() ещё нет. Используй HAVING.
9. Что делает COUNT(*) vs COUNT(col)?
COUNT(*)— считает все строки включая NULLCOUNT(col)— считает только строки где col != NULLCOUNT(DISTINCT col)— количество уникальных ненулевых значений
COUNT(1)работает так же, какCOUNT(*)— разницы в производительности нет, это вопрос стиля.
10. Как посчитать процент от общего итога?
SELECT
category,
SUM(amount) AS revenue,
ROUND(100.0 * SUM(amount) / SUM(SUM(amount)) OVER (), 2) AS pct
FROM orders
GROUP BY category;
Оконные функции
11. Чем оконные функции отличаются от GROUP BY?
GROUP BY схлопывает строки. Оконные функции оставляют все строки, добавляя вычисляемое значение.
-- GROUP BY: 1 строка на отдел
SELECT department, AVG(salary) FROM employees GROUP BY department;
-- Оконная: все строки + средняя по отделу рядом
SELECT name, salary, AVG(salary) OVER (PARTITION BY department) AS dept_avg
FROM employees;
12. Чем RANK() отличается от DENSE_RANK() и ROW_NUMBER()?
| Функция | При одинаковых значениях |
|---|---|
ROW_NUMBER() | Уникальный номер, произвольный порядок |
RANK() | Одинаковый ранг, пропускает следующий (1,1,3) |
DENSE_RANK() | Одинаковый ранг, без пропусков (1,1,2) |
ROW_NUMBER() — когда нужен ровно один результат на группу (топ-1). RANK() — для рейтингов с учётом мест (несколько победителей). DENSE_RANK() — когда пропуски в нумерации нежелательны (призовые места: 1, 2, 3 без пропусков).
13. Что такое LAG и LEAD?
LAG(col, n) — значение из предыдущей строки (на n позиций назад).
LEAD(col, n) — значение из следующей строки.
SELECT date, revenue,
LAG(revenue) OVER (ORDER BY date) AS prev_revenue,
revenue - LAG(revenue) OVER (ORDER BY date) AS delta
FROM daily_stats;
Подзапросы и CTE
14. В чём разница между подзапросом и CTE?
CTE (WITH) — именованный подзапрос, определяется один раз и может переиспользоваться. Лучше читается. Поддерживает рекурсию.
15. Что такое коррелированный подзапрос?
Подзапрос, который ссылается на столбец из внешнего запроса. Выполняется для каждой строки внешнего запроса — может быть медленным.
SELECT name, salary
FROM employees e
WHERE salary > (
SELECT AVG(salary) FROM employees WHERE department = e.department
);
Индексы и производительность
16. Зачем нужны индексы?
Ускоряют поиск и сортировку. Без индекса — полное сканирование таблицы (seq scan). С индексом — поиск по B-tree.
Минус: индексы замедляют INSERT/UPDATE/DELETE и занимают место.
Особенно эффективны индексы на столбцах с высокой селективностью (много уникальных значений). Индекс на поле с тремя значениями (status, gender) обычно бесполезен — планировщик выберет seq scan.
17. Когда индекс не используется?
- Функция над индексированным столбцом:
WHERE LOWER(name) = 'иван' - Неявное приведение типов:
WHERE id = '123'(id — integer) LIKE '%суффикс%'(ноLIKE 'префикс%'— использует)- Слишком маленькая таблица — дешевле seq scan
ORмежду индексированным и неиндексированным столбцом — планировщик может проигнорировать индекс
18. Что такое EXPLAIN и зачем он нужен?
EXPLAIN показывает план выполнения запроса: как БД будет его выполнять, какие индексы использует, ожидаемое количество строк.
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42;
ANALYZE — выполняет запрос и показывает реальное время.
Транзакции
19. Что такое ACID?
- Atomicity — всё или ничего
- Consistency — данные остаются корректными
- Isolation — транзакции не мешают друг другу
- Durability — после COMMIT данные сохранены
20. Что такое уровни изоляции транзакций?
| Уровень | Грязное чтение | Неповторяемое чтение | Фантомное чтение |
|---|---|---|---|
| READ UNCOMMITTED | ✅ возможно | ✅ | ✅ |
| READ COMMITTED | ❌ | ✅ возможно | ✅ |
| REPEATABLE READ | ❌ | ❌ | ✅ возможно |
| SERIALIZABLE | ❌ | ❌ | ❌ |
По умолчанию в PostgreSQL — READ COMMITTED.
В PostgreSQL
REPEATABLE READтакже защищает от фантомного чтения в рамках одного запроса благодаря snapshot isolation, но не от аномалий сериализации при конкурентных вставках.
Хочешь проверить себя? Попробуй решить задачи по этим темам в нашем тренажёре — там 150+ задач с разбором.