Перейти к основному содержимому
Модуль 5 · Урок 2 из 418/57 в курсе~10 min
Уроки модуля (2/4)

Self-JOIN: таблица сама с собой

Иногда отношения, которые вы хотите установить, не между двумя разные таблицы, но между строками одной таблицы. Типичный пример: А. Иерархия «категория → подкатегория», где родительский и дочерний элементы являются строками та же таблица categories, связанная с помощью parent_id.

Чтобы прочитать вместе одну строку (дочернюю) и «ее» другую строку (родительскую), вы используете самосоединение: одна и та же таблица появляется дважды в FROM, причем два разные псевдонимы.

Синтаксис

SQL
SELECT colonne
FROM   tabella AS alias_a
JOIN   tabella AS alias_b ON alias_a.foreign_key = alias_b.id;

Псевдоним обязателен: если мы опустим AS a / AS b, PostgreSQL не будет иметь способ узнать, какую из двух копий мы имеем в виду, когда пишем name или id.

SQL
-- Ogni sotto-categoria con il nome della categoria padre:
SELECT child.name  AS subcategory,
       parent.name AS category
FROM   categories AS child
JOIN   categories AS parent ON child.parent_id = parent.id;

Прочтите простым языком: "возьмите строки categories с именем child и для каждого из них найдите строку categories с именем parent, id которой является parent_id ребенка".

Результат исключает корневые категории (те, у которых есть parent_id = NULL). потому что INNER JOIN пропускает строки, не имеющие совпадений. Если вы хотите включить их, используйте LEFT JOIN.

Самостоятельно присоединитесь с помощью LEFT, чтобы «сохранить корни тоже»

SQL
-- Tutte le categorie, con nome del padre o NULL se sono radice:
SELECT child.name AS category,
       parent.name AS parent
FROM   categories AS child
LEFT JOIN categories AS parent ON child.parent_id = parent.id;

Теперь вы получаете 9 строк (каждая категория в наборе данных) с parent = NULL. для 3 корней.

Агрегация с помощью самосоединения

Он очень хорошо сочетается с GROUP BY, чтобы спросить: «Сколько детей делает каждый у родителя есть":

SQL
SELECT p.name AS parent_category,
       COUNT(c.id) AS children
FROM   categories AS p
LEFT JOIN categories AS c ON c.parent_id = p.id
WHERE  p.parent_id IS NULL          -- solo le radici
GROUP BY p.name
ORDER BY p.name;

Обратите внимание на две важные детали:

  1. WHERE p.parent_id IS NULL фильтрует перед JOIN: мы берем только корни как «левая таблица».
  2. COUNT(c.id) (не COUNT(*)) возвращает 0 для корней без дочерних элементов, тогда как COUNT(*) вернет 1 из-за строки LEFT JOIN с НУЛЬ.

Попробуйте сами

Упражнение#sql.m5.l2.e1
Попыток: 0Загрузка…

Для каждой отдельной категории (категория с родительским идентификатором NOT NULL) вы можете указать имя отдельной категории и имя своей категории. Должное двоеточие: подкатегория, категория. Ордина по категории, пои по подкатегории.

Загрузка редактора…
Показать подсказку

Условие самостоятельного ПРИСОЕДИНЕНИЯ — child.parent_id = родительский.id.

Решение доступно после 3 попыток

Обзор упражнения

Упражнение#sql.m5.l2.e2
Попыток: 0Загрузка…

Для каждой категории (parent_id IS NULL) отображается имя и номер отдельной категории. Должное колоние: имя, дети. Ордина по имени. Включите в конечном итоге одну картинку с 0.

Загрузка редактора…
Показать подсказку

LEFT JOIN для категорий дважды (псевдоним p для родительского элемента, c для дочернего элемента). Используйте COUNT(c.id), а не COUNT(*), чтобы получить 0 для корней без дочерних элементов.

Решение доступно после 3 попыток