Условие
Дана таблица 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 ...;Подводные камни
- Циклы в иерархии — если сотрудник по ошибке менеджер сам себе или цикл
A→B→A, рекурсивный CTE будет бесконечно рекурсировать. Защита:WHERE depth < NилиCYCLEв стандартном SQL. - Топ-менеджер (
manager_id IS NULL) — отвалится приINNER JOIN. ИспользуйтеLEFT JOIN, если нужно показать всех. - Производительность — на десятках миллионов строк 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.