Что такое SELF JOIN в SQL и для чего он используется?

Ответ

SELF JOIN — это операция SQL-соединения (JOIN), при которой таблица соединяется сама с собой. Это необходимо, когда нужно сравнить или связать строки внутри одной и той же таблицы на основе какого-либо условия.

Ключевой момент: При выполнении SELF JOIN необходимо использовать псевдонимы таблиц (алиасы), чтобы можно было ссылаться на разные "экземпляры" одной таблицы в одном запросе.

Классический пример: Иерархия сотрудников Предположим, есть таблица employees:

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    manager_id INT NULL, -- Ссылается на id другого сотрудника в этой же таблице
    FOREIGN KEY (manager_id) REFERENCES employees(id)
);

Задача: Получить список всех сотрудников вместе с именами их менеджеров.

SELECT
    emp.name AS employee_name,
    mgr.name AS manager_name
FROM employees emp -- Алиас для таблицы "сотрудник"
LEFT JOIN employees mgr -- Алиас для таблицы "менеджер"
    ON emp.manager_id = mgr.id;
Результат: employee_name manager_name
Alice Bob
Bob Carol
Carol NULL (у CEO нет менеджера)

Другие практические случаи использования SELF JOIN в Node.js приложениях:

  1. Поиск пар или дубликатов: Найти пользователей, живущих в одном городе.
    SELECT a.name, b.name, a.city
    FROM users a
    INNER JOIN users b ON a.city = b.city AND a.id < b.id;
  2. Анализ последовательностей событий: В таблице логов найти пары последовательных действий одного пользователя.
    SELECT prev.action AS previous_action, curr.action AS current_action
    FROM user_logs curr
    JOIN user_logs prev ON curr.user_id = prev.user_id
        AND curr.timestamp > prev.timestamp
        AND NOT EXISTS (
            SELECT 1 FROM user_logs mid
            WHERE mid.user_id = curr.user_id
            AND mid.timestamp > prev.timestamp
            AND mid.timestamp < curr.timestamp
        );
  3. Организация древовидных структур (adjacency list): Как в примере с сотрудниками. Для работы с глубокими иерархиями в Node.js часто используют рекурсивные запросы (WITH RECURSIVE в PostgreSQL) или специализированные паттерны (Nested Sets, Materialized Path).

Ответ 18+ 🔞

Блин, слушай, вот есть такая штука в SQL — SELF JOIN. Понимаешь, это когда таблица, такая вот хитрая жопа, соединяется сама с собой. Ну то есть не с другой, а вот прям сама с собой, как будто в зеркало смотрится и разговаривает. Зачем? А вот когда надо строки внутри одной и той же таблицы сравнить или связать по какому-то условию. Удивление пиздец, но иногда это реально нужно.

Важнейший момент, без которого нихуя не получится: Нужно обязательно использовать псевдонимы таблиц (алиасы). Иначе как ты будешь отличать, где у тебя в запросе "левая" копия таблицы, а где "правая"? Это как близнецов различить — без имён нихуя не понятно.

Классика жанра: Кто над кем начальник Допустим, есть таблица employees про работничков:

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    manager_id INT NULL, -- Это ссылка на id другого чувака ИЗ ЭТОЙ ЖЕ ТАБЛИЦЫ, ёпта!
    FOREIGN KEY (manager_id) REFERENCES employees(id)
);

Задача: Вывести всех сотрудничков и имена их боссов.

SELECT
    emp.name AS employee_name,   -- Это наш рядовой чувак
    mgr.name AS manager_name     -- А это его шеф, который на него орет
FROM employees emp -- Алиас для таблицы "сотрудник" (employee)
LEFT JOIN employees mgr -- Алиас для таблицы "менеджер" (manager). Одна таблица, но два псевдонима!
    ON emp.manager_id = mgr.id; -- Связываем подчинённого с его начальником
Что получится: employee_name manager_name
Алёна Борис
Борис Валера
Валера NULL (а у главного начальника босса нет, он сам себе царь и бог)

Где ещё эта фигня пригодится в Node.js приложении:

  1. Найти соседей или дубли: Например, найти пользователей из одного города, чтобы познакомить.
    SELECT a.name, b.name, a.city
    FROM users a
    INNER JOIN users b ON a.city = b.city AND a.id < b.id; -- Условие `a.id < b.id` чтобы не выводить пару (Вася, Петя) и (Петя, Вася) как две разные
  2. Отследить цепочку событий: В логах пользователя найти, что он делал сразу перед текущим действием. Тут уже мозг надо включать.
    SELECT prev.action AS previous_action, curr.action AS current_action
    FROM user_logs curr
    JOIN user_logs prev ON curr.user_id = prev.user_id
        AND curr.timestamp > prev.timestamp
        AND NOT EXISTS ( -- Эта подзапрос гарантирует, что между действиями не было других
            SELECT 1 FROM user_logs mid
            WHERE mid.user_id = curr.user_id
            AND mid.timestamp > prev.timestamp
            AND mid.timestamp < curr.timestamp
        );
  3. Работа с деревьями (списки смежности): Как в примере с сотрудниками. Но если иерархия глубокая, в Node.js часто юзают рекурсивные запросы (в PostgreSQL это WITH RECURSIVE) или другие, более навороченные, но быстрые структуры вроде Nested Sets. А то с обычным SELF JOIN'ом на глубоком дереве можно овердохуища времени потратить.