Разработка и Конфигурация · 11 мин
Зацикливание уровней в 1С: что это такое и как его устранить
Зацикливание уровней в 1С: причины циклических ссылок в иерархических справочниках и коде, способы найти и устранить зацикливание без потери данных.
Зацикливание уровней в 1С — это ситуация, когда элемент иерархической структуры прямо или косвенно ссылается сам на себя: группа становится родителем самой себя, подразделение оказывается вложено в собственное подчинённое подразделение, а номенклатурная группа замыкается в кольцо через цепочку родителей. Платформа 1С:Предприятие при обходе такой структуры не может определить конец цепочки и либо уходит в бесконечную рекурсию, либо выдаёт ошибку вида «Зацикливание уровней» при выполнении запроса с итогами по иерархии.
Проблема встречается в иерархических справочниках (номенклатура, контрагенты, подразделения, статьи затрат), в планах счетов, в рекурсивных процедурах прикладного кода и при обменах данными, когда в разных базах родителя элемента назначили по-разному. Последствия варьируются от зависания отчёта и ошибки проведения документа до полной остановки фоновых заданий и невозможности открыть список справочника.
Материал ниже объясняет, откуда берётся зацикливание, как его обнаружить штатными средствами, как исправить данные вручную и программно, и какие проверки стоит встроить в конфигурацию, чтобы ситуация не повторялась.
Что именно называют зацикливанием уровней
В иерархическом справочнике каждый элемент хранит ссылку на родителя в поле Родитель. Нормальная структура — это дерево: у каждого элемента не более одного родителя, а у корня родителя нет. Зацикливание возникает, когда, двигаясь по ссылкам «вверх», вы рано или поздно возвращаетесь к исходному элементу: А → Б → В → А. Дерево превращается в граф с циклом, и понятие «уровень вложенности» теряет смысл — уровень становится бесконечным.
Типичный симптом — сообщение платформы «Ошибка при выполнении запроса: зацикливание уровней» или аналогичное при построении запроса с конструкцией ИТОГИ ПО ... ИЕРАРХИЯ, при обращении к виртуальным таблицам с иерархией либо при выводе списка справочника в режиме дерева. В коде, написанном вручную, зацикливание проявляется как переполнение стека из-за бесконечной рекурсии.
Важно отличать зацикливание данных от зацикливания логики. В первом случае «битая» иерархия хранится в базе и мешает любым механизмам, которые её обходят. Во втором случае данные корректны, но процедура вызывает сама себя без условия выхода — это ошибка разработки, а не данных.
Откуда берётся зацикливание: типовые причины
Чаще всего цикл появляется при обмене данными между базами. В распределённой системе один и тот же элемент может редактироваться в двух узлах: в центральной базе группе «Товары» назначили родителя «Каталог», а в периферийной — наоборот. После очередной синхронизации обе ссылки складываются в кольцо. Особенно этому подвержены обмены через универсальный формат и самописные выгрузки без контроля ссылочной целостности.
Вторая частая причина — ручное или пакетное изменение родителя без проверки. Пользователь через групповое изменение реквизитов переносит группу внутрь её собственной подгруппы, а платформа по умолчанию не всегда блокирует такое действие, если в конфигурации не написана проверка в обработчике ПередЗаписью.
Третий источник — программные ошибки: обработки загрузки из Excel, конвертации данных, восстановления после сбоев, где родитель вычисляется по наименованию или коду и при совпадениях подставляется некорректная ссылка. Реже цикл создаётся прямым редактированием данных через внешние инструменты в обход логики платформы.
Проверить, какой справочник участвует в запросе
Проверить справочники, по которым идёт группировка
Проверить условие выхода из рекурсии и данные
Сравнить родителей элемента в обоих узлах
Как найти зацикленные элементы
Самый надёжный способ — программный обход: для каждого элемента справочника идти по цепочке родителей вверх, запоминая пройденные ссылки в соответствии. Если очередной родитель уже есть в соответствии — цикл найден, и его участников можно вывести в отчёт. Такая проверка выполняется на копии базы или в монопольном режиме и занимает секунды даже на больших справочниках.
Функция НайтиЗацикливание(Ссылка)
Пройденные = Новый Соответствие;
Текущий = Ссылка;
Пока ЗначениеЗаполнено(Текущий) Цикл
Если Пройденные[Текущий] <> Неопределено Тогда
Возврат Истина; // цикл найден
КонецЕсли;
Пройденные.Вставить(Текущий, Истина);
Текущий = Текущий.Родитель;
КонецЦикла;
Возврат Ложь;
КонецФункцииАльтернатива без кода — запрос с соединением таблицы справочника самой с собой по цепочке родителей. Прямой запрос «элемент = родитель своего родителя» ловит только короткие циклы длиной два-три звена; длинные кольца так не находятся, поэтому обход в коде предпочтительнее. Также полезен простой частный случай: выбрать элементы, у которых Ссылка = Родитель, — самоссылка ловится одним запросом мгновенно.
- 1Сделайте резервную копию базы или работайте на копии
- 2Найдите элементы с совпадающими Ссылка и Родитель простым запросом
- 3Запустите обход всех элементов с запоминанием пройденных ссылок
- 4Зафиксируйте список элементов, входящих в кольцо
- 5Определите, какой из элементов должен быть корнем или потомком по смыслу учёта
Как исправить зацикливание в данных
Исправление сводится к разрыву кольца: одному из элементов цикла назначается корректный родитель — либо пустая ссылка (перенос в корень), либо группа, которая гарантированно не входит в цикл. Выбор элемента для разрыва определяется смыслом учёта: обычно разрывают ту связь, которая появилась последней по времени или пришла из обмена.
Правку можно выполнить вручную через форму элемента, если цикл короткий и элементы известны. Если зацикленных участков много (типично после массовой загрузки), используют обработку: она находит кольца и для каждого назначает родителя по заданному правилу — например, переносит в корень все элементы, у которых обнаружен цикл, чтобы затем бухгалтер разнёс их вручную.
После правки обязательно перепроверьте: повторный обход не должен находить циклов, проблемный запрос с итогами по иерархии должен выполняться без ошибки, а отчёты, которые зависали, — строиться за обычное время. Если база участвует в обмене, исправление нужно перенести во все узлы, иначе при следующей синхронизации цикл вернётся.
Зацикливание в программном коде
Отдельный класс проблем — рекурсивные процедуры, обходящие дерево без защиты от циклов. Даже при корректных данных рекурсия опасна переполнением стека на очень глубоких иерархиях, а при зацикленных данных гарантированно уходит в бесконечный вызов. Платформа прерывает такую рекурсию ошибкой, но фоновое задание при этом аварийно завершается.
Защита строится двумя приёмами. Первый — передавать в рекурсию соответствие уже посещённых ссылок и прекращать обход при повторном входе. Второй — заменить рекурсию итеративным циклом с явным стеком в массиве: так контролируется и глубина, и повторные посещения, и поведение предсказуемо на любых данных.
Профилактика: как не допустить повторения
Основная мера — проверка в обработчике ПередЗаписью элемента справочника: перед сохранением подняться от нового родителя вверх по цепочке и убедиться, что среди предков нет самого записываемого элемента. Если найден — отказать в записи с понятным сообщением. Это блокирует создание циклов и вручную, и из обработок загрузки, если они пишут данные штатными методами.
Для обменов данными профилактика другая: определить главный узел для справочника, где иерархия редактируется, а в остальных узлах запретить изменение родителя. Дополнительно полезно регламентное задание, которое периодически обходит ключевые справочники и сообщает администратору о найденных кольцах до того, как пользователи столкнутся с ошибкой в отчёте.
Проверка результата и границы самостоятельной работы
Критерий успешного исправления — тривиальный: обход любого элемента справочника доходит до корня за конечное число шагов. Практически это подтверждается повторным запуском поиска циклов (результат — пусто), выполнением запроса, который раньше падал, и построением отчёта, который зависал. Если всё три пункта пройдены, данные в норме.
Как проверить результат
Циклы не найдены ни в одном элементе
Найдены новые или оставшиеся кольца
Выполняется без ошибки зацикливания
Ошибка воспроизводится
Строится за обычное время
Зависает или растёт потребление памяти
Синхронизация проходит, цикл не возвращается
После обмена кольцо появляется снова
Самостоятельно безопасно искать циклы и исправлять их на копии базы, а также точечно править очевидные самоссылки по согласованию с владельцем учёта. Привлекать специалиста стоит, когда циклы массовые (последствия загрузки или обмена), когда зацикливание затрагивает план счетов или другие объекты, влияющие на регламентированную отчётность, и когда нужно встроить проверки в конфигурацию, снятую с поддержки.
Что важно запомнить
Зацикливание уровней — это кольцо в ссылках «Родитель» иерархического объекта. Находится обходом с запоминанием пройденных ссылок, исправляется разрывом кольца переназначением родителя, предотвращается проверкой в ПередЗаписью и контролем обменов. Любые массовые правки — только после резервной копии.
Частые вопросы
Может ли зацикливание появиться «само», без действий пользователей?
Да, чаще всего через обмены данными: два узла независимо меняют родителей связанных элементов, и после синхронизации ссылки замыкаются в кольцо. Также цикл может появиться при восстановлении данных из архива другой версии или при работе сторонних обработок загрузки. Поэтому после любых массовых операций с иерархией стоит запускать проверку.
Опасно ли зацикливание для остальных данных базы?
Непосредственно цикл портит только ту иерархию, в которой находится: запросы и отчёты по этому справочнику падают или зависают. Косвенный риск в том, что документы, ссылающиеся на зацикленные элементы, могут не проводиться, если их логика обходит иерархию. Другие таблицы и справочники зацикливание не затрагивает.
Почему запрос с ИТОГИ ПО ИЕРАРХИЯ падает, а обычный список открывается нормально?
Плоский список не обходит цепочку родителей — он просто читает записи. Итоги по иерархии требуют от платформы построить дерево и вычислить уровень каждого элемента, что при кольце невозможно. Поэтому ошибка проявляется только в механизмах, реально обходящих иерархию.
Как найти цикл, если элементов в справочнике сотни тысяч?
Обход в коде с соответствием посещённых ссылок работает быстро даже на больших объёмах, если получать родителей запросом пакетно, а не по одному элементу. Практический приём: сначала выгрузить пары «элемент — родитель» одним запросом в таблицу значений, а затем искать циклы уже в памяти, без обращений к базе.
Нужно ли исправлять цикл в обоих узлах распределённой базы?
Да. Если исправить иерархию только в одном узле, при следующем обмене данные из второго узла снова создадут кольцо. Правильная последовательность: определить главный узел для справочника, исправить иерархию в нём, запретить изменение родителей в подчинённых узлах и выполнить синхронизацию.
Можно ли запретить создание циклов на уровне конфигурации?
Да, через обработчик ПередЗаписью элемента справочника: при записи проверяется, не встречается ли сам элемент среди предков нового родителя, и при обнаружении запись отклоняется с отказом. Это закрывает ручной ввод и штатные обработки, но не защищает от обменов — их контролируют отдельно настройкой правил и главного узла.