Вопросы по SQL на собеседовании — часть 1/3: Junior с разбором ответов
Это часть 1 из 3. Часть 2: Middle → | Часть 3: Senior →
Вопросы по SQL на собеседовании — часть 1/3: Junior с разбором ответов
TL;DR: 8 вопросов по SQL, которые стабильно задают джуниорам на собеседованиях — по отзывам с Glassdoor, Хабра и анализу 9 000+ технических интервью за 2024–2026. Для каждого: что на самом деле проверяют, как ответить и где кандидаты ошибаются.
Как пользоваться: Открой перед собеседованием → пройдись по каждому вопросу → попробуй ответить ДО того, как прочитаешь разбор → проверь follow-up. Если follow-up вызывает затруднение — это зона роста.
Эта подборка — не пересказ документации. Это вопросы, которые реально звучат на собеседованиях по SQL в 2024–2026 годах — по данным Glassdoor, тредов на Reddit и анализу вакансий на HH и Хабре. SQL входит в топ-4 тем на технических интервью: по данным анализа 9 247 собеседований, вопросы по базам данных и SQL встречаются в 11% всех технических интервью, а задачи на оптимизацию запросов — в 19% бэкенд-собесов.
Для каждого вопроса: что интервьюер на самом деле проверяет, как ответить, пример кода и где большинство кандидатов ошибается.
Серия из трёх частей: Junior (эта статья) → Middle → Senior. Начнём с базы.
Содержание
- 1. Что такое PRIMARY KEY и FOREIGN KEY?
- 2. В чём разница между WHERE и HAVING?
- 3. Какие типы JOIN существуют?
- 4. Как работает GROUP BY?
- 5. В чём разница между DELETE, TRUNCATE и DROP?
- 6. Что такое NULL и почему = NULL не работает?
- 7. В чём разница между UNION и UNION ALL?
- 8. Как найти дубликаты в таблице?
1. Что такое PRIMARY KEY и FOREIGN KEY?
Интервьюер проверяет: понимаешь ли ты основы реляционной модели и связи между таблицами.
PRIMARY KEY — столбец (или комбинация столбцов), который однозначно идентифицирует каждую строку в таблице. Два правила: значения уникальны и не могут быть NULL.
FOREIGN KEY — столбец, который ссылается на PRIMARY KEY другой таблицы. Обеспечивает ссылочную целостность: нельзя вставить значение, которого нет в родительской таблице.
CREATE TABLE departments (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
department_id INT,
FOREIGN KEY (department_id) REFERENCES departments(id)
);
Теперь нельзя добавить сотрудника с department_id = 999, если в departments нет записи с id = 999.
Типичная ошибка: говорят, что PRIMARY KEY — это всегда один столбец. На самом деле ключ может быть составным:
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);
Follow-up: Может ли таблица существовать без PRIMARY KEY? Технически — да, СУБД не запрещает. Но на практике это антипаттерн: без PK невозможно однозначно идентифицировать строку.
2. В чём разница между WHERE и HAVING?
Интервьюер проверяет: знаешь ли ты порядок выполнения SQL-запроса.
WHERE фильтрует строки до группировки. HAVING фильтрует группы после агрегации.
-- WHERE: отбираем только заказы за 2025 год
SELECT customer_id, COUNT(*) AS order_count
FROM orders
WHERE created_at >= '2025-01-01'
GROUP BY customer_id;
-- HAVING: из результатов оставляем только тех, у кого > 5 заказов
SELECT customer_id, COUNT(*) AS order_count
FROM orders
WHERE created_at >= '2025-01-01'
GROUP BY customer_id
HAVING COUNT(*) > 5;
Типичная ошибка: пытаются использовать агрегатную функцию в WHERE:
-- Так НЕ работает
SELECT customer_id, COUNT(*) AS order_count
FROM orders
WHERE COUNT(*) > 5 -- ошибка!
GROUP BY customer_id;
WHERE выполняется ДО GROUP BY — на этом этапе агрегатных значений ещё нет.
Follow-up: Можно ли использовать WHERE и HAVING в одном запросе? Да, и это нормальная практика: WHERE отсекает ненужные строки до группировки (что ускоряет запрос), а HAVING фильтрует уже агрегированные результаты.
3. Какие типы JOIN существуют?
Интервьюер проверяет: умеешь ли ты соединять таблицы и понимаешь ли, когда какой JOIN использовать.
Пять основных типов:
INNER JOIN — только строки, где есть совпадение в обеих таблицах.
LEFT JOIN — все строки из левой таблицы + совпадения из правой (NULL, если совпадения нет).
RIGHT JOIN — зеркало LEFT JOIN: все строки из правой таблицы.
FULL JOIN — все строки из обеих таблиц, NULL где совпадений нет.
CROSS JOIN — декартово произведение: каждая строка первой таблицы соединяется с каждой строкой второй.
-- Все сотрудники с их отделами (даже если отдел не назначен)
SELECT e.name, d.name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
Типичная ошибка: ставят условие фильтрации правой таблицы LEFT JOIN в WHERE вместо ON:
-- НЕПРАВИЛЬНО: LEFT JOIN превращается в INNER JOIN
SELECT e.name, d.name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
WHERE d.active = true; -- отсекает строки с NULL!
-- ПРАВИЛЬНО: условие в ON
SELECT e.name, d.name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id AND d.active = true;
Это одна из самых частых ошибок на собеседованиях по SQL (по данным InterviewQuery и разборам на Хабре).
Follow-up: Если таблица A имеет 10 строк, а B — 100, сколько строк вернёт LEFT JOIN из A в B? Минимум 10 (если связь один-к-одному или нет совпадений). Но может быть больше, если связь один-ко-многим.
Мы разобрали связи между таблицами и фильтрацию. Дальше — группировка, удаление данных и работа с NULL. Это то, на чём чаще всего спотыкаются джуниоры.
4. Как работает GROUP BY?
Интервьюер проверяет: умеешь ли ты агрегировать данные и понимаешь ли, как GROUP BY связан с SELECT.
GROUP BY группирует строки с одинаковыми значениями в указанных столбцах. После группировки можно применять агрегатные функции: COUNT, SUM, AVG, MAX, MIN.
-- Количество сотрудников в каждом отделе
SELECT department_id, COUNT(*) AS employee_count
FROM employees
GROUP BY department_id;
-- Средняя зарплата по городам, только города с > 10 сотрудников
SELECT city, AVG(salary) AS avg_salary
FROM employees
GROUP BY city
HAVING COUNT(*) > 10
ORDER BY avg_salary DESC;
Типичная ошибка: добавляют в SELECT столбец, которого нет в GROUP BY и который не обёрнут в агрегатную функцию:
-- Ошибка: name не в GROUP BY и не в агрегатной функции
SELECT department_id, name, COUNT(*)
FROM employees
GROUP BY department_id;
MySQL в режиме по умолчанию (до 5.7) это пропускал, возвращая случайное значение name. PostgreSQL и современный MySQL с ONLY_FULL_GROUP_BY — выдадут ошибку.
Follow-up: Чем GROUP BY отличается от DISTINCT? GROUP BY группирует строки для агрегации, DISTINCT просто убирает дубликаты. Если нужна только уникальность без агрегатных функций — используй DISTINCT.
5. В чём разница между DELETE, TRUNCATE и DROP?
Интервьюер проверяет: понимаешь ли ты уровни «разрушительности» операций и их связь с транзакциями.
-
DELETE — DML-операция. Удаляет строки по условию. Можно откатить (ROLLBACK). Срабатывают триггеры. Медленнее.
-
TRUNCATE — DDL-операция. Удаляет ВСЕ строки. В большинстве СУБД нельзя откатить (в PostgreSQL — можно, если внутри транзакции). Триггеры не срабатывают. Быстрее.
-
DROP — DDL-операция. Удаляет таблицу целиком: данные, структуру, индексы. Необратимо.
DELETE FROM orders WHERE created_at < '2020-01-01'; -- удалить старые заказы
TRUNCATE TABLE temp_logs; -- очистить таблицу логов
DROP TABLE IF EXISTS legacy_data; -- удалить таблицу
Типичная ошибка: говорят, что TRUNCATE нельзя откатить — это зависит от СУБД. В PostgreSQL TRUNCATE транзакционный, в MySQL (InnoDB) — нет.
Follow-up: DELETE без WHERE удалит все строки. Чем это отличается от TRUNCATE? DELETE без WHERE — строка за строкой, с логированием каждой. TRUNCATE — сбрасывает страницы данных разом. На большой таблице разница в скорости — десятки раз.
6. Что такое NULL и почему = NULL не работает?
Интервьюер проверяет: понимаешь ли ты трёхзначную логику SQL.
NULL — это не ноль и не пустая строка. Это отсутствие значения. Любое сравнение с NULL возвращает UNKNOWN (не TRUE и не FALSE).
-- Так НЕ работает
SELECT * FROM users WHERE middle_name = NULL; -- вернёт 0 строк!
-- Правильно
SELECT * FROM users WHERE middle_name IS NULL;
SELECT * FROM users WHERE middle_name IS NOT NULL;
Для подстановки значения вместо NULL используй COALESCE:
SELECT name, COALESCE(middle_name, 'не указано') AS middle_name
FROM users;
Типичная ошибка: забывают про NULL в NOT IN:
-- Опасно! Если в подзапросе есть NULL, результат — 0 строк
SELECT * FROM products
WHERE category_id NOT IN (SELECT category_id FROM hidden_categories);
-- Если hidden_categories содержит NULL — запрос вернёт пустой результат
-- Безопасно
SELECT * FROM products
WHERE category_id NOT IN (
SELECT category_id FROM hidden_categories WHERE category_id IS NOT NULL
);
Follow-up: Что вернёт NULL + 5? NULL. Что вернёт NULL = NULL? UNKNOWN. Любая арифметика и сравнение с NULL даёт NULL/UNKNOWN.
Осталось два вопроса — про объединение результатов и поиск дубликатов. Это практические задачи, которые часто дают прямо на собесе.
7. В чём разница между UNION и UNION ALL?
Интервьюер проверяет: думаешь ли ты о производительности при написании запросов.
Оба оператора объединяют результаты двух SELECT-запросов. Разница:
- UNION — убирает дубликаты (выполняет DISTINCT). Дороже по ресурсам.
- UNION ALL — оставляет все строки как есть. Быстрее.
-- UNION: уникальные города из двух таблиц
SELECT city FROM customers
UNION
SELECT city FROM suppliers;
-- UNION ALL: все города, включая повторы (быстрее)
SELECT city FROM customers
UNION ALL
SELECT city FROM suppliers;
Типичная ошибка: используют UNION по умолчанию, когда дубликаты невозможны или неважны. Это лишняя сортировка и расход ресурсов.
Правило: если не нужна дедупликация — всегда UNION ALL.
Follow-up: Какие требования к запросам в UNION? Одинаковое количество столбцов и совместимые типы данных в каждой позиции.
8. Как найти дубликаты в таблице?
Интервьюер проверяет: можешь ли ты решить практическую задачу, а не просто знаешь теорию.
Классическая задача. GROUP BY + HAVING COUNT > 1:
-- Найти дублирующиеся email
SELECT email, COUNT(*) AS cnt
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
Если нужны сами строки с дубликатами (не только значения):
SELECT u.*
FROM users u
JOIN (
SELECT email
FROM users
GROUP BY email
HAVING COUNT(*) > 1
) dupes ON u.email = dupes.email
ORDER BY u.email;
Типичная ошибка: забывают, что «дубликат» может быть по комбинации столбцов, а не по одному:
-- Дубликаты по комбинации имя + телефон
SELECT first_name, phone, COUNT(*)
FROM contacts
GROUP BY first_name, phone
HAVING COUNT(*) > 1;
Follow-up: Как удалить дубликаты, оставив одну запись? Используй CTE с ROW_NUMBER (это уже Middle-уровень, разберём в части 2).
Итого Junior: 8 вопросов. Фокус — ключи, JOIN, GROUP BY, NULL, удаление данных. Если уверенно отвечаешь на follow-up — база у тебя крепкая. Если NULL + NOT IN стал сюрпризом — перечитай вопрос 6.
Как попробовать Sobes AI
Sobes AI — ИИ-ассистент, который помогает на техническом собеседовании в реальном времени. Подсказывает ответы, пока ты общаешься с интервьюером.
- Зайди на sobesai.app и скачай приложение
- Запусти перед собеседованием
- Sobes AI слушает вопрос и подсказывает структурированный ответ
- Работает с SQL, базами данных и другими техническими темами
Это часть 1 из 3. Часть 2: Middle → | Часть 3: Senior →
Готовитесь к собеседованию?
Sobes AI слушает вопросы интервьюера и генерирует ответы в реальном времени.
Скачать Sobes AI