При интеграции различных систем 1С часто возникает задача передачи иерархических данных (например, справочников Номенклатура или Контрагенты) через HTTP-сервисы. Стандартная проблема заключается в том, что при получении данных в виде «плоской» таблицы значений программисты начинают вручную прописывать уровни вложенности через Родитель.Наименование, Родитель.Родитель.Наименование и так далее. Это приводит к созданию жесткой структуры с фиксированным количеством уровней и появлению неприятных «пустых группировок» в отчетах, если реальная вложенность элементов различается.
В этой статье мы подробно разберем, как уйти от ручного перечисления родителей и заставить Систему компоновки данных (СКД) самостоятельно выстраивать дерево любой вложенности, даже если данные получены из внешнего источника.
Это наиболее правильный и гибкий способ отображения иерархии, когда источником данных выступает внешняя таблица (Набор данных - объект). Вместо того чтобы пытаться передать из удаленной базы готовую древовидную структуру, мы передадим плоский список, где для каждого элемента указан его непосредственный родитель.
Проанализируем алгоритм действий:
Ссылка или UID) и уникальный идентификатор его родителя (РодительUID).ТаблицаЗначений, которая затем передается в СКД. Важно, чтобы типы данных в колонках Ссылка и Родитель совпадали.Разберем по шагам, как настроить связь для построения иерархии:
Ссылка = Родитель (где Ссылка — это поле приемника, а Родитель — поле источника).После этой настройки в настройках отчета (закладка «Настройки» -> «Группировки») вместо обычной группировки по полю Ссылка можно будет выбрать тип группировки «Иерархия». СКД сама рекурсивно обойдет таблицу и построит дерево. Главное преимущество здесь в том, что если у элемента нет родителя, система не будет создавать пустых уровней, что полностью решает проблему «лишних» группировок.
Рассмотрим ситуацию, когда база-источник содержит десятки тысяч элементов с глубокой вложенностью. Передавать полную таблицу и заставлять СКД строить связи на лету может быть накладно для производительности. В таких случаях эффективно использовать математический подход под названием «Транзитивное замыкание».
Суть метода заключается в подготовке на стороне удаленной базы специальной таблицы связей. В этой таблице для каждого элемента хранятся ссылки не только на его непосредственного родителя, но и на всех его «предков» вверх по дереву до самого корня.
Пример структуры такой таблицы:
Узел — ссылка на текущий элемент.Предок — ссылка на одного из родителей в иерархии.Уровень — расстояние от узла до этого предка.Проанализируем преимущества этого подхода:
Во-первых, вы можете одним простым запросом ГДЕ Предок = &НужнаяГруппа получить все дочерние элементы любого уровня вложенности. Во-вторых, это позволяет строить отчеты в «плоском» виде, но с сохранением всей информации об иерархии для фильтрации. Для реализации этого метода на стороне источника (удаленной базы) можно использовать временные таблицы в запросе с соединением справочника самим с собой несколько раз или воспользоваться готовыми решениями по генерации текста запроса для транзитивного замыкания.
Если по каким-то причинам использование «Собственной иерархии» в СКД невозможно, и вам приходится работать с плоской таблицей с колонками Родитель1, Родитель2, мы можем оптимизировать процесс формирования запроса. Рассмотрим, как избежать избыточности.
Вместо того чтобы всегда запрашивать 10 уровней родителя (на всякий случай), выясним реальную максимальную глубину дерева в удаленной базе перед основным запросом. Посмотрим на пример кода, который можно выполнить на стороне источника:
// Определяем максимальный уровень вложенности справочника
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ МАКСИМУМ(Уровень) КАК МаксУровень ИЗ Справочник.Номенклатура";
Результат = Запрос.Выполнить().Выгрузить();
МаксимальнаяГлубина = Результат[0].МаксУровень;
// Динамически формируем текст запроса для HTTP-ответа
ТекстЗапроса = "ВЫБРАТЬ Ссылка КАК Элемент";
Для Итератор = 1 По МаксимальнаяГлубина Цикл
Точки = "";
Для к = 1 По Итератор Цикл
Точки = Точки + ".Родитель";
КонецЦикла;
ТекстЗапроса = ТекстЗапроса + ", Ссылка" + Точки + ".Наименование КАК Родитель" + Итератор;
КонецЦикла;
ТекстЗапроса = ТекстЗапроса + " ИЗ Справочник.Номенклатура";
Такой подход позволяет передавать ровно столько данных, сколько реально существует в базе, минимизируя трафик и количество пустых колонок. Однако помните, что это все равно создает «плоскую» структуру, которая менее удобна для пользователя, чем полноценное дерево в СКД.
Если вы контролируете разработку HTTP-сервиса на стороне источника, рассмотрим вариант передачи данных сразу в древовидном формате JSON. Это избавит принимающую сторону от необходимости логического восстановления связей.
На стороне источника создается рекурсивная функция, которая обходит справочник и формирует структуру объектов Соответствие или Структура, вложенных друг в друга. Пример логики:
Функция ПолучитьВеткуИерархии(Родитель)
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Наименование ИЗ Справочник.Номенклатура ГДЕ Родитель = &Родитель");
Запрос.УстановитьПараметр("Родитель", Родитель);
СписокЭлементов = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ДанныеЭлемента = Новый Структура("Наименование, ВложенныеЭлементы");
ДанныеЭлемента.Наименование = Выборка.Наименование;
ДанныеЭлемента.ВложенныеЭлементы = ПолучитьВеткуИерархии(Выборка.Ссылка);
СписокЭлементов.Добавить(ДанныеЭлемента);
КонецЦикла;
Возврат СписокЭлементов;
КонецФункции
Затем этот массив структур сериализуется в JSON. На стороне приемника вы получаете готовое дерево, которое можно легко преобразовать в ДеревоЗначений и вывести в форму или отчет.
Проанализируем ситуацию, описанную автором темы: у некоторых элементов есть Родитель1 и Родитель2, а у некоторых — нет. При использовании жесткой структуры колонок в отчете неизбежно появятся пустые строки в группировках. Чтобы этого избежать при работе с СКД, всегда стремитесь к использованию одной колонки «Родитель» в связке с механизмом собственной иерархии (Метод 1).
Важный нюанс: Если данные загружаются во временную таблицу (МВТ) или объектный набор данных, убедитесь, что в поле Родитель для корневых элементов записано значение NULL или пустая ссылка соответствующего типа. Если там будет «битая» ссылка или значение неопределенного типа, СКД не сможет корректно сопоставить корень дерева, и иерархия может не отобразиться.
Для решения проблемы получения иерархии из внешней базы наиболее эффективным является сочетание передачи плоской таблицы (ID + ParentID) и настройки Связей наборов данных в СКД. Это позволяет:
.Родитель.Рассмотренные методы транзитивного замыкания и динамической генерации запросов являются отличными вспомогательными инструментами для высоконагруженных систем или специфических требований к формату передачи данных.