DBAtlas · Атлас эксплуатации баз данных

Рекурсивный CTE (WITH RECURSIVE)

Oracle

-- Hierarchy with CONNECT BY:
SELECT id, name, manager_id, LEVEL
FROM employee
START WITH manager_id IS NULL
CONNECT BY PRIOR id = manager_id;

-- Alternative (11gR2+): standard recursive WITH (the RECURSIVE keyword is not written)
WITH tree (id, name, manager_id) AS (
  SELECT id, name, manager_id FROM employee WHERE manager_id IS NULL
  UNION ALL
  SELECT c.id, c.name, c.manager_id
  FROM employee c JOIN tree a ON c.manager_id = a.id
)
SELECT * FROM tree;

Используется для обхода иерархических (древовидных) данных: оргструктур, спецификаций изделий и т. п. Классический способ Oracle — CONNECT BY / PRIOR и псевдостолбец LEVEL; START WITH задаёт корневую строку. С 11gR2 поддерживается и стандартный рекурсивный CTE.

PostgreSQL

WITH RECURSIVE tree (id, name, manager_id, level) AS (
  SELECT id, name, manager_id, 1
  FROM employee WHERE manager_id IS NULL
  UNION ALL
  SELECT c.id, c.name, c.manager_id, a.level + 1
  FROM employee c JOIN tree a ON c.manager_id = a.id
)
SELECT * FROM tree ORDER BY level;

WITH RECURSIVE объединяет якорный запрос и рекурсивную часть через UNION ALL; рекурсивная часть выполняется повторно, пока результат не станет пустым. Идеален для иерархий и обхода графов. Защищайтесь от бесконечных циклов через UNION (вместо ALL) или защиту от циклов.

MySQL

WITH RECURSIVE tree (id, name, manager_id, level) AS (
  SELECT id, name, manager_id, 1
  FROM employee WHERE manager_id IS NULL
  UNION ALL
  SELECT c.id, c.name, c.manager_id, a.level + 1
  FROM employee c JOIN tree a ON c.manager_id = a.id
)
SELECT * FROM tree ORDER BY level;

MySQL 8.0 (и MariaDB 10.2+) поддерживают соответствующий стандарту синтаксис WITH RECURSIVE. Глубина рекурсии ограничена системной переменной cte_max_recursion_depth (по умолчанию 1000). В старых версиях приходилось писать цикл в хранимой процедуре.

SQL Server

WITH tree (id, name, manager_id, level) AS (
  SELECT id, name, manager_id, 1
  FROM employee WHERE manager_id IS NULL
  UNION ALL
  SELECT c.id, c.name, c.manager_id, a.level + 1
  FROM employee c JOIN tree a ON c.manager_id = a.id
)
SELECT * FROM tree ORDER BY level
OPTION (MAXRECURSION 100);

В SQL Server ключевое слово RECURSIVE для рекурсивного CTE не пишется; CTE с UNION ALL, ссылающийся на себя, автоматически считается рекурсивным. Лимит рекурсии по умолчанию — 100; меняется через OPTION (MAXRECURSION n) (0 = без ограничений, используйте осторожно).

MongoDB

// Closest equivalent: the $graphLookup stage
db.employee.aggregate([
  { $match: { manager_id: null } },
  { $graphLookup: {
      from: "employee",
      startWith: "$id",
      connectFromField: "id",
      connectToField: "manager_id",
      as: "subordinates"
  } }
])

В MongoDB нет рекурсивного CTE; для иерархического обхода используется этап агрегации $graphLookup. connectFromField/connectToField задают поля связи, maxDepth — ограничение глубины. Он разрешает древовидные и графовые связи одним запросом.

Redis

# Not applicable: Redis has no recursive queries.

Redis не предлагает языка запросов. Обход иерархий/графов выполняется в коде приложения или на сервере Lua-скриптом; в базовом Redis обход дерева пишется на стороне клиента. (Поддержка ранее использовавшегося модуля RedisGraph прекращена в 2023.)

Elasticsearch

# Not applicable: recursive queries are not supported.
# Approach: model with parent-child (join field) or nested,
# deep hierarchies are traversed on the application side.

Elasticsearch не предлагает рекурсивных запросов (аналога WITH RECURSIVE / CONNECT BY нет). Иерархические связи моделируются типом поля 'join' (parent/child) или хранением пути в дереве (materialized path) в поле; многоуровневый обход обычно выполняется в приложении.

Открыть в DBAtlas →