При разработке конфигураций и отчетов на платформе 1С:Предприятие иногда возникает необходимость обойти последовательную цепочку связанных элементов — например, когда каждая следующая запись ссылается на предыдущую. На классических СУБД подобные задачи решаются рекурсивными запросами с оператором WITH RECURSIVE. Однако встроенный язык запросов 1С не поддерживает рекурсивные конструкции. Проанализируем, как решить эту задачу стандартными средствами платформы.
Рассмотрим исходную задачу: в регистре сведений хранятся пара параметров — измерение Контрагент и ресурс Контрагент. Начальная запись содержит пустое измерение и первичное значение ресурса (например, «Ромашка»). В каждой последующей записи измерение хранит контрагента из ресурса предыдущей записи.
Выясним причину, почему стандартный запрос ВЫБРАТЬ с сортировкой не выдаст правильный порядок. Данная структура данных представляет собой односвязный список. В самой таблице отсутствует явный признак порядка (индекс, числовой номер или дата) и нет сведений о глубине вложенности. Поскольку язык запросов 1С исполняется в рамках одного прохода без динамической рекурсии, обычным запросом последовательно обойти такую цепочку неограниченной длины невозможно.
Наиболее эффективным решением внутри платформы является использование Системы компоновки данных (СКД). Механизм Связи наборов данных умеет обрабатывать иерархические и рекурсивные структуры (включая древовидные и линейные списки).
Разберем по шагам настройку схемы компоновки данных:
Благодаря этому СКД автоматически выполнит необходимое количество итераций рекурсивного связывания и выстроит элементы в строго последовательном порядке.
Если результат нужно получить встроенным языком 1С без вызова СКД, то обход выполняется комбинацией запроса и цикла на сервере. Проанализируем алгоритм такого решения:
Контрагент не заполнено (равно пустой ссылке).Пока выполняем поиск следующей записи, где измерение равно ресурсу с предыдущего шага.Рассмотрим пример реализации на встроенном языке:
ТаблицаРезультата = Новый ТаблицаЗначений;
ТаблицаРезультата.Колонки.Добавить("Контрагент");
ТекущийРодитель = Справочники.Контрагенты.ПустаяСсылка();
ПройденныеЭлементы = Новый Соответствие;
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Первые 1
| Т.РесурсКонтрагент КАК СледующийКонтрагент
|ИЗ
| РегистрСведений.ЦепочкаКонтрагентов КАК Т
|ГДЕ
| Т.ИзмерениеКонтрагент = &ТекущийРодитель";
Запрос.УстановитьПараметр("ТекущийРодитель", ТекущийРодитель);
Выборка = Запрос.Выполнить().Выбрать();
Если НЕ Выборка.Следующий() Тогда
Прервать; // Цепочка закончилась
КонецЕсли;
СледующийЭлемент = Выборка.СледующийКонтрагент;
// Защита от зацикливания
Если ПройденныеЭлементы.Получить(СледующийЭлемент) <> Неопределено Тогда
Прервать;
КонецЕсли;
ПройденныеЭлементы.Вставить(СледующийЭлемент, Истина);
НоваяСтрока = ТаблицаРезультата.Добавить();
НоваяСтрока.Контрагент = СледующийЭлемент;
ТекущийРодитель = СледующийЭлемент;
КонецЦикла;
Сохранение связей «элемент — следующий элемент» в одном регистре без явного порядка делает систему уязвимой к ошибкам и снижает производительность. Если есть возможность изменить архитектуру, рассмотрим более правильные подходы:
ПорядковыйНомер (число). При формировании цепочки записывайте позицию элемента (1, 2, 3...). Тогда выборка сортируется элементарным предложением УПОРЯДОЧИТЬ ПО Т.ПорядковыйНомер ВОЗР.В ИЕРАРХИИ и упорядочивание по иерархии.