JOIN — одна из ключевых тем SQL-собеседования. Умение правильно соединять таблицы отличает кандидата, который «знакомился с SQL», от того, кто реально с ним работает. Эта подборка задач покрывает все важные типы JOIN с готовыми решениями.
Почему JOIN так важен на собеседовании
В реальной работе аналитик постоянно объединяет данные из разных таблиц: заказы с клиентами, события с пользователями, продажи с регионами. Без уверенного владения JOIN написать полезный запрос почти невозможно.
Проверяйте не только синтаксис, но и понимание: когда использовать INNER vs LEFT, как избежать дубликатов, что такое декартово произведение.
Задача 1: Базовый INNER JOIN (Junior)
-- Схема:
-- employees(id, name, department_id, salary)
-- departments(id, name, budget)
-- Задача: выведите имя сотрудника, название отдела и зарплату.
-- Отсортируйте по зарплате по убыванию.
SELECT
e.name AS employee_name,
d.name AS department_name,
e.salary
FROM employees e
INNER JOIN departments d ON d.id = e.department_id
ORDER BY e.salary DESC;
Что проверяем: базовый синтаксис JOIN, алиасы, ORDER BY.
Красный флаг: кандидат пишет FROM employees, departments WHERE employees.department_id = departments.id — старый синтаксис, но технически работает. Уточните, знает ли он современный JOIN.
Задача 2: LEFT JOIN — найти «сирот» (Junior+)
-- Задача: найдите отделы, в которых нет ни одного сотрудника.
SELECT d.id, d.name
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id
WHERE e.id IS NULL;
Что проверяем: понимание LEFT JOIN, работа с NULL.
Распространённая ошибка:
-- Неверно: это вернёт отделы с сотрудниками
SELECT d.id, d.name
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id
WHERE e.department_id IS NULL; -- правильно, но e.id IS NULL проще
Задача 3: Несколько JOIN (Middle)
-- Схема:
-- orders(id, customer_id, product_id, quantity, order_date)
-- customers(id, name, city)
-- products(id, name, price, category)
-- Задача: выведите город, категорию продукта и суммарную выручку.
-- Только заказы за 2026 год.
SELECT
c.city,
p.category,
SUM(o.quantity * p.price) AS total_revenue
FROM orders o
JOIN customers c ON c.id = o.customer_id
JOIN products p ON p.id = o.product_id
WHERE o.order_date >= '2026-01-01'
AND o.order_date < '2027-01-01'
GROUP BY c.city, p.category
ORDER BY total_revenue DESC;
Что проверяем: цепочка из трёх JOIN, фильтрация дат, агрегация.
Задача 4: SELF JOIN (Middle)
SELF JOIN — соединение таблицы с собой. Нужен для иерархических данных.
-- Схема: employees(id, name, manager_id)
-- manager_id ссылается на id в той же таблице
-- Задача: выведите пары "сотрудник — имя его руководителя".
-- Для сотрудников без руководителя выведите NULL.
SELECT
e.name AS employee_name,
m.name AS manager_name
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id;
Что проверяем: понимание рекурсивных структур, SELF JOIN.
Задача 5: Коварный JOIN с дубликатами (Middle+)
-- Схема:
-- orders(id, customer_id, amount)
-- payments(id, order_id, amount, status)
-- У одного заказа может быть несколько платежей (частичные оплаты)
-- Задача: найдите заказы, где сумма платежей меньше суммы заказа
SELECT
o.id,
o.amount AS order_amount,
COALESCE(SUM(p.amount), 0) AS paid_amount
FROM orders o
LEFT JOIN payments p ON p.order_id = o.id AND p.status = 'success'
GROUP BY o.id, o.amount
HAVING COALESCE(SUM(p.amount), 0) < o.amount;
Ловушка: если кандидат не группирует по o.amount, а пишет условие в WHERE вместо HAVING — задайте уточняющий вопрос.
Задача 6: Anti-JOIN (Senior)
-- Задача: найдите клиентов, которые НИКОГДА не делали заказ
-- Способ 1: LEFT JOIN + IS NULL
SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;
-- Способ 2: NOT EXISTS (часто эффективнее)
SELECT c.id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);
-- Способ 3: NOT IN (опасен при NULL в подзапросе!)
SELECT id, name FROM customers
WHERE id NOT IN (SELECT customer_id FROM orders WHERE customer_id IS NOT NULL);
Senior должен знать все три способа и объяснить разницу в производительности.
Шкала оценки по JOIN
| Задача | Уровень | Максимум баллов |
|---|---|---|
| INNER JOIN | Junior | 2 |
| LEFT JOIN + NULL | Junior+ | 2 |
| Несколько JOIN | Middle | 3 |
| SELF JOIN | Middle | 3 |
| JOIN с дубликатами | Middle+ | 4 |
| Anti-JOIN все способы | Senior | 4 |
Итого 18 баллов. Junior ≥ 7, Middle ≥ 12, Senior ≥ 16.
Вопросы для обсуждения
После выполнения задач задайте теоретические вопросы:
- «В чём разница между INNER JOIN и CROSS JOIN?»
- «Что такое декартово произведение и когда оно возникает случайно?»
- «Когда LEFT JOIN может вернуть больше строк, чем в исходной таблице?»
Кандидат, уверенно отвечающий на эти вопросы, реально понимает JOIN, а не просто заучил синтаксис.
Готовые интерактивные задачи на JOIN доступны на SQLlab.ru — идеально для самостоятельной подготовки кандидатов перед собеседованием.