SQLLab
Все статьи

SQL JOIN задачи для технического скрининга

Набор практических SQL JOIN задач для технического скрининга кандидатов: INNER, LEFT, SELF JOIN и антипаттерны.

12 февраля 2026 г.·4 мин чтения·

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 JOINJunior2
LEFT JOIN + NULLJunior+2
Несколько JOINMiddle3
SELF JOINMiddle3
JOIN с дубликатамиMiddle+4
Anti-JOIN все способыSenior4

Итого 18 баллов. Junior ≥ 7, Middle ≥ 12, Senior ≥ 16.

Вопросы для обсуждения

После выполнения задач задайте теоретические вопросы:

  • «В чём разница между INNER JOIN и CROSS JOIN?»
  • «Что такое декартово произведение и когда оно возникает случайно?»
  • «Когда LEFT JOIN может вернуть больше строк, чем в исходной таблице?»

Кандидат, уверенно отвечающий на эти вопросы, реально понимает JOIN, а не просто заучил синтаксис.

Готовые интерактивные задачи на JOIN доступны на SQLlab.ru — идеально для самостоятельной подготовки кандидатов перед собеседованием.

Похожие статьи

Попробуй на практике

Тренажёр с реальными задачами — бесплатно и без регистрации

Открыть тренажёр →