# Рекурсивные запросы в PostgreSQL: Иерархии, графы и не только

postgres 7 / 8
3 min read
Table of Contents

Рекурсивные запросы в PostgreSQL: Иерархии, графы и не только

Рекурсивные запросы — мощный инструмент PostgreSQL для работы с иерархическими структурами и графами. Они позволяют обрабатывать данные, где элементы связаны друг с другом через родительско-дочерние отношения, например, организационные структуры, деревья категорий или пути в социальных сетях. В этой статье мы разберем, как работают рекурсивные CTE (Common Table Expressions), их синтаксис, примеры использования и подводные камни.


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

Рекурсивный CTE (Common Table Expression) — это временный результат запроса, который может ссылаться на самого себя. Он состоит из двух частей:

  1. Нерекурсивный член (Anchor Member) — начальный запрос, возвращающий базовый набор данных.
  2. Рекурсивный член (Recursive Member) — часть, которая ссылается на CTE и расширяет результат до достижения условия остановки.

Синтаксис:

WITH RECURSIVE cte_name AS (
-- Нерекурсивный член
SELECT ... FROM ...
UNION ALL
-- Рекурсивный член
SELECT ... FROM cte_name JOIN ...
)
SELECT * FROM cte_name;

Пример 1: Иерархия сотрудников

Представим таблицу employees, где каждый сотрудник может иметь менеджера:

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

Задача: Найти всех подчиненных сотрудника с id = 1, включая косвенных.

Решение:

WITH RECURSIVE subordinates AS (
-- Anchor Member: начальный сотрудник (менеджер)
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE id = 1
UNION ALL
-- Recursive Member: подчиненные на каждом уровне
SELECT e.id, e.name, e.manager_id, s.depth + 1
FROM employees e
INNER JOIN subordinates s ON e.manager_id = s.id
)
SELECT * FROM subordinates;

Результат будет включать все уровни подчинения с указанием глубины (depth).


Как это работает?

  1. Шаг 1: Выполняется нерекурсивный член. Результат — начальная строка (сотрудник с id = 1).
  2. Шаг 2: Рекурсивный член объединяется с текущим результатом CTE. На каждой итерации добавляются подчиненные предыдущего уровня.
  3. Шаг 3: Процесс повторяется, пока рекурсивный член не вернет пустой набор.

Пример 2: Вычисление факториала

Рекурсивные запросы можно использовать и для математических вычислений. Например, факториал числа 5:

WITH RECURSIVE factorial(n, result) AS (
SELECT 5, 1 -- Начальное значение: 5! = 5 * 4 * ... * 1
UNION ALL
SELECT n - 1, result * n
FROM factorial
WHERE n > 1
)
SELECT result FROM factorial WHERE n = 1;

Результат: 120.


Осторожно: Бесконечные циклы!

Если иерархия содержит циклы (например, сотрудник A подчиняется B, а B подчиняется A), запрос может зациклиться. Чтобы избежать этого:

  • Используйте массив для отслеживания посещенных узлов.
  • Ограничьте глубину рекурсии.

Пример с проверкой циклов:

WITH RECURSIVE hierarchy AS (
SELECT id, name, manager_id, ARRAY[id] AS path
FROM employees
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.manager_id, h.path || e.id
FROM employees e
JOIN hierarchy h ON e.manager_id = h.id
WHERE NOT e.id = ANY(h.path) -- Предотвращение циклов
)
SELECT * FROM hierarchy;

Оптимизация и ограничения

  • Производительность: Рекурсивные запросы могут быть медленными на больших данных. Для деревьев используйте специализированные подходы (например, Nested Sets).
  • Глубина рекурсии: PostgreSQL ограничивает глубину рекурсии параметром max_recursive_iterations (по умолчанию 100000). При необходимости его можно изменить.

Заключение

Рекурсивные CTE в PostgreSQL — гибкий инструмент для обработки иерархий и графов. Они позволяют:

  • Строить древовидные структуры.
  • Находить пути в графах.
  • Решать задачи, требующие итеративных вычислений.

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

Next: 1. Встроенное расширение pg_stat_statements
Аватар автора

Спасибо, что прочитали статью. Посмотрите другие материалы в архиве, там много практических разборов по разным технологиям.


postgres Series

# 1. B-tree (Balanced Tree)

postgres 1 / 8
2 min read

Типы индексов в PostgreSQL: полный обзор и рекомендации по выбору Оптимизация запросов через правильное использование индексов Введение Индексы в PostgreSQL — это мощный инструмент для ускорения…

Read

# Работа с данными из нескольких таблиц в PostgreSQL: полное руководство

postgres 2 / 8
2 min read

PostgreSQL — мощная реляционная СУБД, предоставляющая множество инструментов для выборки данных из нескольких таблиц. В этой статье рассмотрим ключевые методы с примерами и советами по оптимизации.…

Read

# Что такое транзакция?

postgres 3 / 8
3 min read

Транзакции в PostgreSQL: Основы, Управление и Лучшие Практики Введение Транзакции — фундаментальный механизм обеспечения целостности данных в реляционных базах. В PostgreSQL они играют ключевую роль,…

Read

# Основы оконных функций

postgres 4 / 8
3 min read

Оконные функции в PostgreSQL: мощный инструмент аналитики Оконные функции (Window Functions) в PostgreSQL — это продвинутый инструмент для выполнения вычислений над группами строк, связанных с…

Read

# 1. Типы блокировок в PostgreSQL

postgres 5 / 8
3 min read

Блокировки в PostgreSQL: механизмы управления параллельным доступом к данным Введение В многопользовательских средах базы данных, таких как PostgreSQL, блокировки играют ключевую роль в обеспечении…

Read

# 1. Использование индексов

postgres 6 / 8
3 min read

Оптимизация запросов в PostgreSQL: лучшие практики для повышения производительности PostgreSQL — мощная СУБД с широкими возможностями, но даже она может столкнуться с проблемами производительности…

Read

# Рекурсивные запросы в PostgreSQL: Иерархии, графы и не только

postgres 7 / 8
3 min read

Рекурсивные запросы — мощный инструмент PostgreSQL для работы с иерархическими структурами и графами. Они позволяют обрабатывать данные, где элементы связаны друг с другом через родительско-дочерние…

Continue