0Pricing
SQL Academy · Урок

Как работают рекурсивные CTE

Базовый случай плюс рекурсивный шаг

«Как работают рекурсивные CTE» — бесплатный урок SQL Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Academy содержит 4 уроков всего.

Что такое рекурсивное CTE

Рекурсивное CTE — это общее табличное выражение, которое ссылается само на себя. Оно позволяет писать запросы, повторяющие один шаг до выполнения условия, — подобно циклу, но в виде обычного SQL.

Рекурсивные CTE определяются с помощью ключевого слова WITH RECURSIVE и идеально подходят для обхода иерархических или подобных графам данных, например организационных структур, деревьев папок и спецификаций изделий.

Структура из двух частей

Каждое рекурсивное CTE состоит ровно из двух частей, разделённых оператором UNION ALL:

1. Базовый случай — нерекурсивный SELECT, возвращающий исходные строки.

2. Рекурсивный шаг — SELECT, который соединяет CTE с самим собой и формирует строки следующего уровня.

Механизм базы данных продолжает выполнять рекурсивный шаг и накапливать результаты, пока тот не перестанет возвращать новые строки.

WITH RECURSIVE cte_name AS (
  -- Base case
  SELECT ...
  UNION ALL
  -- Recursive step (references cte_name)
  SELECT ... FROM source JOIN cte_name ON ...
)
SELECT * FROM cte_name;

Подсчёт от 1 до 5

Самый простой рекурсивный CTE подсчитывает числа. В базовом случае задаётся начальное значение 1. На каждом шаге рекурсии добавляется 1. Предложение WHERE внутри рекурсивного шага служит условием завершения — без него запрос выполнялся бы бесконечно.

WITH RECURSIVE counter(n) AS (
  SELECT 1
  UNION ALL
  SELECT n + 1 FROM counter WHERE n < 5
)
SELECT n FROM counter;

Пошаговое выполнение

Вот как механизм базы данных обрабатывает CTE-счётчик на каждой итерации:

Итерация 0 (базовый случай): возвращает {1}.

Итерация 1: применяет рекурсивный шаг к {1} и возвращает {2}.

Итерация 2: применяет рекурсивный шаг к {2} и возвращает {3}.

Итерации 3 и 4: возвращают сначала {4}, затем {5}.

Итерация 5: условие WHERE n < 5 ложно для n=5, поэтому возвращается ноль строк. Запрос завершается.

Все накопленные строки — 1, 2, 3, 4, 5 — составляют итоговый результат.

Настройка таблицы иерархии

Рекурсивные CTE особенно эффективны для таблиц, ссылающихся на самих себя. Создадим таблицу employees, в которой у каждого сотрудника может быть необязательное поле manager_id, указывающее на строку в той же таблице.

CREATE TABLE employees (
  id       INTEGER PRIMARY KEY,
  name     VARCHAR(50),
  manager_id INTEGER REFERENCES employees(id)
);

INSERT INTO employees VALUES
  (1, 'Alice',   NULL),
  (2, 'Bob',     1),
  (3, 'Carol',   1),
  (4, 'Dave',    2),
  (5, 'Eve',     2),
  (6, 'Frank',   3);

Обход иерархии

Теперь можно пройти всю цепочку подчинения, начиная с CEO (Alice, id=1). В базовом случае выбирается Alice, а рекурсивный шаг находит всех сотрудников, чей manager_id совпадает с уже имеющимся в CTE идентификатором.

Результат включает каждого сотрудника, достижимого от Alice, независимо от глубины дерева.

WITH RECURSIVE org_tree AS (
  SELECT id, name, manager_id, 0 AS depth
  FROM employees
  WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, e.manager_id, ot.depth + 1
  FROM employees e
  JOIN org_tree ot ON e.manager_id = ot.id
)
SELECT depth, name FROM org_tree ORDER BY depth, name;

Отслеживание пути

Распространённое улучшение — построение строки пути, показывающей всю цепочку от корня до каждого узла. По мере углубления рекурсии мы объединяем имена, разделяя их с помощью ' -> '.

Это упрощает отображение навигации в виде цепочки ссылок и отладку глубоких иерархий.

WITH RECURSIVE org_tree AS (
  SELECT id, name, name AS path
  FROM employees
  WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, ot.path || ' -> ' || e.name
  FROM employees e
  JOIN org_tree ot ON e.manager_id = ot.id
)
SELECT name, path FROM org_tree ORDER BY path;

Ограничение глубины рекурсии

Глубокие или циклические данные могут заставить рекурсивное CTE выполняться очень долго. Используйте две безопасные практики:

1. Отслеживайте глубину и добавляйте предложение WHERE — WHERE depth < 10 гарантирует, что обход не выйдет за пределы 10 уровней.

2. Используйте столбец для обнаружения циклов — некоторые базы данных (PostgreSQL 14+) поддерживают синтаксис CYCLE, автоматически обнаруживающий повторные посещения узлов.

WITH RECURSIVE org_tree AS (
  SELECT id, name, 0 AS depth
  FROM employees
  WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, ot.depth + 1
  FROM employees e
  JOIN org_tree ot ON e.manager_id = ot.id
  WHERE ot.depth < 10
)
SELECT depth, name FROM org_tree;

UNION и UNION ALL в рекурсивных CTE

В рекурсивном шаге почти всегда используется UNION ALL, а не UNION. Вот почему:

UNION удаляет дубликаты строк после каждой итерации, сравнивая весь набор результатов. Это чрезвычайно затратно и может изменить смысл для графов, в которых один и тот же узел действительно достижим по нескольким путям.

UNION ALL сохраняет все строки без удаления дубликатов, что быстрее и правильно при обходе дерева. Используйте UNION только при конкретной необходимости удалить дубликаты и если Вы понимаете связанные с этим затраты производительности.

Формирование последовательности дат

Рекурсивные CTE также удобны для формирования последовательностей дат. Этот пример создаёт запись для каждого дня заданной недели — такой подход часто используется для построения календарных отчётов или заполнения пропусков во временных рядах.

WITH RECURSIVE date_series AS (
  SELECT DATE '2024-01-01' AS day
  UNION ALL
  SELECT day + INTERVAL '1 day'
  FROM date_series
  WHERE day < DATE '2024-01-07'
)
SELECT day FROM date_series;

Поиск всех подчинённых одного руководителя

В базовом случае можно указать любой конкретный узел, а не только корневой. Здесь мы начинаем с Bob (id=2) и находим всех, кто подчиняется ему прямо или косвенно.

Этот подход полезен для проверок прав доступа, агрегаций по поддереву или ограничения данных на панели мониторинга одним отделом.

WITH RECURSIVE subordinates AS (
  SELECT id, name
  FROM employees
  WHERE id = 2
  UNION ALL
  SELECT e.id, e.name
  FROM employees e
  JOIN subordinates s ON e.manager_id = s.id
)
SELECT name FROM subordinates;

Быстрая проверка

Проверьте, насколько хорошо Вы поняли принцип работы рекурсивных CTE.

Итоги урока

В этом уроке Вы узнали, как работают рекурсивные CTE:

Структура: каждое рекурсивное CTE содержит базовый случай (исходные строки), соединённый с рекурсивным шагом (самоссылочным SELECT) с помощью UNION ALL.

Завершение: механизм базы данных повторяет рекурсивный шаг и накапливает результаты, пока этот шаг не вернёт ноль строк.

Распространённые применения: обход организационных структур и деревьев папок, формирование последовательностей чисел или дат, вычисление путей и поиск всех узлов в поддереве.

Советы по безопасности: всегда добавляйте условие завершения (ограничение глубины или защиту от циклов) и для повышения производительности предпочитайте UNION ALL оператору UNION.

Часто задаваемые вопросы

Урок «Как работают рекурсивные CTE» бесплатный?

Да — полный текст урока «Как работают рекурсивные CTE» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Academy, подпишись на CoddyKit PRO. Курс SQL Academy содержит 4 уроков всего.

Чему я научусь в уроке «Как работают рекурсивные CTE»?

Базовый случай плюс рекурсивный шаг Ты практикуешь SQL Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать SQL Academy?

Предыдущий опыт не требуется. SQL Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Как работают рекурсивные CTE»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке SQL Academy?

Да. Каждый урок SQL Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Как работают рекурсивные CTE
  2. Обход дерева категорий
  3. Генерация рядов и последовательностей
  4. Как избежать бесконечных циклов
← Назад к SQL Academy