Самосоединение (self-join)

Self-join: как соединить таблицу саму с собой для иерархий и поиска пар.

Self-join — это JOIN таблицы с самой собой. Чтобы различать «две копии», им дают разные алиасы.

Зачем нужно

Если в таблице есть ссылка на строку той же таблицы (сотрудник → его руководитель, категория → родитель), достать связанную строку можно только соединив таблицу саму с собой.

Иерархия: сотрудник и его руководитель

В таблице employees колонка manager_id ссылается на id другого сотрудника. Соединим таблицу с собой: одна копия — сотрудник (e), вторая — его руководитель (m).

CREATE TABLE employees (
    id         INTEGER PRIMARY KEY,
    name       TEXT,
    manager_id INTEGER     -- ссылка на id другого сотрудника
);
INSERT INTO employees (id, name, manager_id) VALUES
    (1, 'Директор', NULL),
    (2, 'Аня',  1),
    (3, 'Борис',1),
    (4, 'Вера', 2);

SELECT e.name AS sotrudnik, m.name AS rukovoditel
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
ORDER BY e.id;

Вывод:

Директор|
Аня|Директор
Борис|Директор
Вера|Аня

У каждого сотрудника подставилось имя его руководителя. У Директора руководителя нет — взяли LEFT JOIN, чтобы он не выпал, и в колонке rukovoditel у него NULL.

Поиск пар без повторов

Другой классический случай — найти все пары строк, например игроков из одного города. Чтобы не получить дубли (A–B и B–A) и пары с самим собой, добавляют условие a.id < b.id.

CREATE TABLE players (id INTEGER PRIMARY KEY, name TEXT, city TEXT);
INSERT INTO players (name, city) VALUES
    ('Аня','Москва'), ('Борис','Москва'), ('Вера','Казань'), ('Гена','Москва');

-- Все пары игроков из одного города, без повторов и без пары с собой
SELECT a.name AS igrok1, b.name AS igrok2, a.city
FROM players a
JOIN players b ON a.city = b.city AND a.id < b.id
ORDER BY a.city, a.name, b.name;

Вывод:

Аня|Борис|Москва
Аня|Гена|Москва
Борис|Гена|Москва

Условие a.id < b.id делает три вещи сразу: убирает пару игрока с самим собой, убирает зеркальный дубль (Аня–Борис и Борис–Аня — это одна пара) и фиксирует порядок внутри пары.

Итог

  • Self-join — соединение таблицы с собой; обязательно разные алиасы для двух «копий».
  • Иерархии (сотрудник→руководитель) разворачиваются через self-join; берите LEFT JOIN, чтобы вершина не выпала.
  • Для пар без повторов добавляйте a.id < b.id.
Проверьте себя
1. Что обязательно при self-join?
AУказать тип FULL JOIN
BДать таблице два разных алиаса
CИспользовать DISTINCT
DСоздать индекс
2. Зачем при поиске пар добавляют условие a.id < b.id?
AДля скорости
BЧтобы убрать пары с собой и зеркальные дубли
CЧтобы отсортировать результат
DЭто требование синтаксиса
3. Почему для иерархии сотрудник-руководитель часто берут LEFT JOIN?
AТак быстрее
BЧтобы вершина иерархии (без руководителя) не выпала из результата
CINNER JOIN не работает с одной таблицей
DЧтобы избежать дубликатов
Поддержать проект