Как получить упорядоченную рекурсивную цепочку элементов в запросе 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке конфигураций и отчетов на платформе 1С:Предприятие иногда возникает необходимость обойти последовательную цепочку связанных элементов — например, когда каждая следующая запись ссылается на предыдущую. На классических СУБД подобные задачи решаются рекурсивными запросами с оператором WITH RECURSIVE. Однако встроенный язык запросов 1С не поддерживает рекурсивные конструкции. Проанализируем, как решить эту задачу стандартными средствами платформы.

Ограничения языка запросов и сущность проблемы

Рассмотрим исходную задачу: в регистре сведений хранятся пара параметров — измерение Контрагент и ресурс Контрагент. Начальная запись содержит пустое измерение и первичное значение ресурса (например, «Ромашка»). В каждой последующей записи измерение хранит контрагента из ресурса предыдущей записи.

Выясним причину, почему стандартный запрос ВЫБРАТЬ с сортировкой не выдаст правильный порядок. Данная структура данных представляет собой односвязный список. В самой таблице отсутствует явный признак порядка (индекс, числовой номер или дата) и нет сведений о глубине вложенности. Поскольку язык запросов 1С исполняется в рамках одного прохода без динамической рекурсии, обычным запросом последовательно обойти такую цепочку неограниченной длины невозможно.

Решение 1: Использование связей наборов данных в СКД

Наиболее эффективным решением внутри платформы является использование Системы компоновки данных (СКД). Механизм Связи наборов данных умеет обрабатывать иерархические и рекурсивные структуры (включая древовидные и линейные списки).

Разберем по шагам настройку схемы компоновки данных:

  1. Создадим набор данных - запрос, который выбирает пары «Измерение — Ресурс» из регистра сведений.
  2. Перейдем на вкладку Связи наборов данных.
  3. Добавим связь этого набора данных самого с собой (указав в качестве источника и приемника один и тот же набор данных).
  4. В поле Выражение источник укажем ресурс (следующий контрагент), а в поле Выражение приемник — измерение (предыдущий контрагент).
  5. В качестве начального условия (поле Параметр или условие связи) зададим отбор, где измерение не заполнено (пустая ссылка).
  6. На вкладке Настройки добавим структуру в виде группировки с включенным иерархическим выводом или выводом деревом.

Благодаря этому СКД автоматически выполнит необходимое количество итераций рекурсивного связывания и выстроит элементы в строго последовательном порядке.

Решение 2: Программно-запросный обход в цикле

Если результат нужно получить встроенным языком 1С без вызова СКД, то обход выполняется комбинацией запроса и цикла на сервере. Проанализируем алгоритм такого решения:

  1. Первым запросом получаем начальную запись, у которой измерение Контрагент не заполнено (равно пустой ссылке).
  2. Сохраняем полученный ресурс во временный массив или таблицу значений.
  3. В цикле Пока выполняем поиск следующей записи, где измерение равно ресурсу с предыдущего шага.
  4. Для предотвращения бесконечной цикличности при повреждении данных проверяем, не встречался ли контрагент ранее.

Рассмотрим пример реализации на встроенном языке:


ТаблицаРезультата = Новый ТаблицаЗначений;
ТаблицаРезультата.Колонки.Добавить("Контрагент");

ТекущийРодитель = Справочники.Контрагенты.ПустаяСсылка();
ПройденныеЭлементы = Новый Соответствие;

Пока Истина Цикл
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ Первые 1
        |	Т.РесурсКонтрагент КАК СледующийКонтрагент
        |ИЗ
        |	РегистрСведений.ЦепочкаКонтрагентов КАК Т
        |ГДЕ
        |	Т.ИзмерениеКонтрагент = &ТекущийРодитель";
    
    Запрос.УстановитьПараметр("ТекущийРодитель", ТекущийРодитель);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Если НЕ Выборка.Следующий() Тогда
        Прервать; // Цепочка закончилась
    КонецЕсли;
    
    СледующийЭлемент = Выборка.СледующийКонтрагент;
    
    // Защита от зацикливания
    Если ПройденныеЭлементы.Получить(СледующийЭлемент) <> Неопределено Тогда
        Прервать;
    КонецЕсли;
    
    ПройденныеЭлементы.Вставить(СледующийЭлемент, Истина);
    
    НоваяСтрока = ТаблицаРезультата.Добавить();
    НоваяСтрока.Контрагент = СледующийЭлемент;
    
    ТекущийРодитель = СледующийЭлемент;
КонецЦикла;

Решение 3: Оптимизация структуры данных

Сохранение связей «элемент — следующий элемент» в одном регистре без явного порядка делает систему уязвимой к ошибкам и снижает производительность. Если есть возможность изменить архитектуру, рассмотрим более правильные подходы:

  1. Добавление порядкового номера: Внесите в регистр сведений ресурс ПорядковыйНомер (число). При формировании цепочки записывайте позицию элемента (1, 2, 3...). Тогда выборка сортируется элементарным предложением УПОРЯДОЧИТЬ ПО Т.ПорядковыйНомер ВОЗР.
  2. Табличная часть справочника: Если цепочки контрагентов формируются в рамках конкретных документов или процессов, целесообразно хранить их в табличной части справочника или документа, где порядок строк гарантируется самой платформой.
  3. Иерархический справочник: Если связи отражают отношения подчинения, используйте справочник с включенной иерархией элементов. В этом случае в запросах доступен стандартный оператор В ИЕРАРХИИ и упорядочивание по иерархии.
← На главную