Собесов

Self-join: вывести сотрудников и их менеджеров

SQLJOINЛёгкаяJunior

Условие

Дана таблица employees(employee_id, name, manager_id), где manager_id — это ссылка на employee_id другого сотрудника. Напишите запрос, который выведет имена всех сотрудников, у которых есть менеджер, вместе с именами их менеджеров.

Решение

Self-join

Та же таблица соединяется сама с собой — один экземпляр играет роль «сотрудник», другой — «менеджер».

SELECT e1.name AS employee_name,
       e2.name AS manager_name
FROM   employees e1
JOIN   employees e2
       ON e1.manager_id = e2.employee_id;

INNER JOIN отбросит сотрудников без менеджера (где manager_id IS NULL). Если нужны и они — LEFT JOIN:

SELECT e1.name AS employee_name,
       e2.name AS manager_name
FROM   employees e1
LEFT JOIN employees e2
       ON e1.manager_id = e2.employee_id;
-- сотрудник без менеджера получит NULL

Глубже: иерархия N уровней

Self-join даёт только один уровень. Чтобы получить «менеджер менеджера», нужны несколько self-join'ов или рекурсивный CTE:

WITH RECURSIVE chain AS (
  SELECT employee_id, name, manager_id, 1 AS depth
  FROM   employees
  WHERE  employee_id = 7         -- стартовый сотрудник
  UNION ALL
  SELECT e.employee_id, e.name, e.manager_id, c.depth + 1
  FROM   employees e
  JOIN   chain c ON c.manager_id = e.employee_id
)
SELECT * FROM chain;

Алиасы — обязательно

Без алиасов СУБД не поймёт, какой name имеется в виду:

-- ошибка: ambiguous column
SELECT name FROM employees JOIN employees ON ...;

Подводные камни

  1. Циклы в иерархии — если сотрудник по ошибке менеджер сам себе или цикл A→B→A, рекурсивный CTE будет бесконечно рекурсировать. Защита: WHERE depth < N или CYCLE в стандартном SQL.
  2. Топ-менеджер (manager_id IS NULL) — отвалится при INNER JOIN. Используйте LEFT JOIN, если нужно показать всех.
  3. Производительность — на десятках миллионов строк self-join без индекса по manager_id будет медленным.

Эталонный ответ

SELECT e1.name AS employee_name, e2.name AS manager_name
FROM   employees e1
JOIN   employees e2 ON e1.manager_id = e2.employee_id;

Self-join: одна таблица, два алиаса (e1 для сотрудника, e2 для менеджера). INNER JOIN отсекает сотрудников без менеджера; LEFT JOIN сохраняет их с NULL в manager_name.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти