Блог
SQL

Self-join в SQL: как соединить таблицу саму с собой

Self-join (самосоединение) — это JOIN таблицы с самой собой, где одна и та же таблица участвует в запросе дважды под разными псевдонимами (алиасами). Используется, когда в одной таблице есть строки, которые связаны друг с другом через столбец, ссылающийся на ту же таблицу — например, сотрудник и его руководитель, или город и соседний город в таблице маршрутов.

Как сделать self-join на примере сотрудников и их руководителей?

SELECT
e.name AS employee_name,
m.name AS manager_name
FROM employees e
JOIN employees m ON e.manager_id = m.id;

Таблица employees участвует в запросе два раза — один раз под алиасом e (сотрудник), другой раз под алиасом m (руководитель). Условие ON e.manager_id = m.id соединяет строку сотрудника со строкой того же самого руководителя, у которого id совпадает со значением manager_id сотрудника. Без разных алиасов для одной и той же таблицы SQL не смог бы понять, к какому «экземпляру» таблицы относится каждый столбец в условии — алиасы обязательны для self-join, в отличие от обычного JOIN двух разных таблиц, где они не всегда нужны.

Как найти сотрудников без руководителя через LEFT JOIN self-join?

SELECT
e.name AS employee_name,
m.name AS manager_name
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;

Обычный JOIN (INNER JOIN) в self-join исключит из результата сотрудников, у которых manager_id равен NULL (например, генеральный директор, у которого нет руководителя) — для них не найдётся пары в правой части соединения. LEFT JOIN сохраняет такие строки в результате, подставляя NULL в столбец manager_name, если руководителя нет. Тот же принцип, что и для self-join, применим и к обычному JOIN между разными таблицами: если нужно сохранить строки без пары — используется LEFT JOIN, а не INNER JOIN.

Как сравнить строки таблицы друг с другом через self-join?

SELECT
a.name AS product_a,
b.name AS product_b,
a.price
FROM products a
JOIN products b ON a.price = b.price AND a.id < b.id;

Self-join часто используется, чтобы найти пары строк с одинаковым значением какого-то столбца — здесь товары с одинаковой ценой. Условие a.id < b.id, а не просто a.id != b.id, — важная деталь. Если убрать условие на id вообще (оставить только a.price = b.price), товар попадёт в пару сам с собой (a.id = b.id), потому что цена совпадает сама с собой. a.id != b.id от самосравнения защищает полностью, но не решает вторую проблему: каждая пара товаров с одинаковой ценой встретится в результате дважды — сначала как a=товар1, b=товар2, потом как a=товар2, b=товар1. a.id < b.id закрывает обе проблемы одним условием: строка не может сравниться сама с собой (id не может быть меньше самого себя), а из каждой пары в результат попадает только одна комбинация, где id первого товара меньше id второго.

Когда self-join не хватает для иерархии в несколько уровней?

Self-join в базовом виде находит связь только на один уровень вглубь (сотрудник → его непосредственный руководитель). Если нужна вся цепочка вверх до самого верха иерархии (сотрудник → руководитель → руководитель руководителя → и так далее, произвольная глубина), одного self-join недостаточно — либо нужно делать self-join столько раз, сколько уровней иерархии заранее известно (негибко и многословно), либо использовать WITH RECURSIVE (рекурсивный CTE), который умеет проходить иерархию произвольной глубины за один запрос.

Какой JOIN выбрать для разных сценариев self-join?

— Найти сотрудника вместе с его руководителем. INNER JOIN (self-join), условие e.manager_id = m.id — сотрудники, у которых руководитель указан.
— Не потерять сотрудников без руководителя. LEFT JOIN (self-join), то же условие — плюс генеральный директор с manager_id = NULL.
— Сравнить строки таблицы друг с другом. INNER JOIN (self-join), условие a.price = b.price AND a.id < b.id — пары товаров с одинаковой ценой без дублей.
— Пройти иерархию произвольной глубины. WITH RECURSIVE (это уже не просто self-join) — вся цепочка подчинения снизу вверх.

Частые вопросы про self-join

Обязательно ли использовать алиасы в self-join? Да, обязательно. Без алиасов SQL не сможет различить, к какому из двух экземпляров одной и той же таблицы относится каждый столбец в условии ON и в SELECT — запрос либо вызовет ошибку неоднозначности, либо СУБД вообще не разрешит написать одно и то же имя таблицы дважды в FROM/JOIN без алиасов.
Self-join — это отдельный вид JOIN, вроде INNER или LEFT JOIN? Нет. Self-join — это не отдельный тип соединения, а способ использования обычного JOIN (INNER, LEFT, любого другого), при котором соединяемые «таблицы» на самом деле являются одной и той же таблицей. Синтаксис JOIN/ON не отличается от соединения двух разных таблиц.
Как понять, что задаче нужен именно self-join? Признак — в таблице есть столбец, который ссылается на первичный ключ той же самой таблицы (например, manager_id ссылается на id внутри employees), или нужно сравнить строки одной таблицы друг с другом по какому-то критерию (одинаковая цена, соседние даты, дубликаты).
Self-join медленнее обычного JOIN? Сам механизм соединения работает одинаково независимо от того, разные это таблицы или одна и та же — производительность зависит от размера таблицы, наличия индексов на столбцах условия ON и от того, сколько пар строк реально соответствует условию, а не от того, что это именно self-join.

Закрепить self-join на практике

Self-join путает новичков не синтаксисом, а необходимостью держать в голове, что оба алиаса — e и m — это одна и та же таблица. Быстрее всего это укладывается в голове через практику: от простого сравнения строк одной таблицы до иерархии сотрудник-менеджер и рекурсивного обхода произвольной глубины через WITH RECURSIVE. Задачи на self-join и рекурсивные CTE есть на тренажёре SQL Arena от Quality Academy — больше 800 задач, часть бесплатно, AI-ментор подсказывает, если условие в ON перепутано.

-------

Полезные ссылки школы

Сайт Quality Academy:
/main
Telegram-канал:
https://t.me/quality_academy
YouTube-канал:
https://www.youtube.com/@quality_academy
ВКонтакте:
https://vk.com/quality_academy
Канал отзывов учеников (58+ отзывов):
https://t.me/quality_academy_reviews
Задать вопрос менеджеру:
https://t.me/quality_academy_bot
Тест «Подойдёт ли вам тестирование»:
https://quiz.quality-academy.ru
5000+ вопросов с собесов тестировщика — бот-тренажёр:
https://t.me/quality_academy_interview_bot
3 практические задачи и дорожная карта:
https://t.me/quality_academy_tasks_bot
Бесплатные тренажёры — SQL Arena, Playwright Arena, Python Arena:
SQL Arena · Playwright Arena · Python Arena