Рекурсивный CTE и цикл в иерархии: как не уйти в бесконечный обход

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

Как разобрать ситуацию

Для небольшого учебного графа переносим строковый путь с разделителями. Перед переходом проверяем, встречался ли следующий id именно как целая вершина. Разделители важны: без них идентификатор 1 ошибочно совпадёт с 11. Дополнительно ограничим глубину, чтобы повреждённые данные не раздували вычисление.

Пример и ожидаемый результат

Есть связи 1→2, 2→3 и 3→1. Обход от 1 должен вернуть пути к 1, 2 и 3, после чего остановиться. Повторное посещение 1 не является новым элементом дерева.

WITH RECURSIVE edges(a,b) AS(VALUES(1,2),(2,3),(3,1)), walk(id,path,depth) AS(
SELECT 1,',1,',0 
UNION ALL 
SELECT e.b,w.path||e.b||',',w.depth+1 
FROM walk w 
JOIN edges e ON e.a=w.id 
WHERE instr(w.path,','||e.b||',')=0 AND w.depth<10) 
SELECT id 
FROM walk 
ORDER BY depth;

Запрос возвращает узлы 1, 2 и 3 по одному разу для выбранного пути. Циклическое ребро не расширяет рекурсию.

Проверка перед применением

  • Добавьте соседнюю вершину 11 и проверьте, что проверка не путает её с 1.
  • Протестируйте прямую петлю 1→1.
  • Отдельно сохраните сигнал о найденном цикле: молчаливое исключение ребра не исправляет справочник.

Договоритесь с владельцем данных о поведении отчёта при повреждении: остановить публикацию, показать неполный результат с предупреждением или отправить запись на разбор. Просто увеличить лимит глубины опасно: это откладывает симптом и не делает циклическую структуру корректным деревом.

Границы решения

Строковый путь не является универсальным оптимальным представлением графа. Для больших графов и сложных ключей нужны другие структуры. Здесь использованы целочисленные id и синтаксис SQLite.

Если запросы уже пишутся, а уверенности в кардинальности, оконных рамках и проверке результата не хватает, продолжить практику можно в нашем платном курсе

Продвинутый SQL: сложные запросы и задачи для собеседований. Перед записью сравните программу со своей задачей и подготовкой.

Техническая справка: www.sqlite.org: документация.

Следующий шаг: Сложные SQL-запросы: разборы расчётов и ошибок. По смежным вопросам: Фильтр дат SQL: как включить весь месяц и не захватить следующий, Как разбить события на сессии в SQL по паузе между действиями.