Как построить дерево значений в запросе по двум разным справочникам в 1С с помощью СКД?

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

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

Сложность заключается в том, что стандартная иерархия 1С работает «из коробки» только в пределах одного справочника. Нам же необходимо объединить данные из двух абсолютно разных таблиц: иерархического справочника СправочникСсылка.ПодразделенияОрганизаций и подчиненного, но неиерархического справочника СправочникСсылка.ШтатноеРасписание.

Многие разработчики по привычке начинают решать эту задачу «в лоб»: пишут сложный запрос, выгружают плоскую ТаблицаЗначений, а затем с помощью медленных рекурсивных функций в цикле программно перекладывают строки в ДеревоЗначений. Мы подробно разберем, почему этот путь неоптимален, и покажем, как элегантно решить эту задачу с помощью Системы компоновки данных (СКД), получив готовое дерево значений напрямую из процессора вывода.

Выясним причину, почему стандартные подходы не работают, и разберем по шагам правильную технологию реализации.

---

Почему рекурсивный обход в коде — это плохой путь?

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

  1. Низкая производительность. С ростом объема данных (например, если в компании тысячи сотрудников и сотни подразделений) время выполнения рекурсивного перебора растет нелинейно. Это приводит к долгому ожиданию формирования отчета.
  2. Сложность поддержки кода. Если завтра бизнес-требования изменятся и потребуется добавить еще один уровень иерархии (например, конкретных физических лиц или проекты), придется полностью переписывать алгоритм рекурсии.
  3. Проблемы с расчетом итогов. В ручном дереве значений вам придется самостоятельно вычислять математические итоги (суммы ресурсов, количество ставок и т.д.) по родительским веткам, учитывая иерархию.

Всех этих проблем можно легко избежать, если переложить построение связей и расчет итогов на мощный встроенный движок — СКД.

---

Шаг 1: Проектирование запроса и подготовка списка смежности

Чтобы СКД смогла построить дерево по разнородным объектам, нам необходимо представить данные в виде единой плоской таблицы связей. В теории баз данных эта структура называется списком смежности (Adjacency List). Каждая строка такой таблицы должна содержать информацию о текущем узле и его непосредственном родителе.

Спроектируем запрос, который объединит данные по организациям, подразделениям и штатному расписанию. Основная идея заключается в приведении ключевых полей к единому составному типу данных. Мы выделим два главных поля:

Рассмотрим подробнее пример такого запроса:


ВЫБРАТЬ
    Организации.Ссылка КАК ЭлементСхемы,
    НЕОПРЕДЕЛЕНО КАК Родитель,
    "Организация" КАК ТипУзла
ИЗ
    Справочник.Организации КАК Организации

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Подразделения.Ссылка КАК ЭлементСхемы,
    ВЫБОР
        КОГДА Подразделения.Родитель = ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка)
            ТОГДА Подразделения.Владелец
        ИНАЧЕ Подразделения.Родитель
    КОНЕЦ КАК Родитель,
    "Подразделение" КАК ТипУзла
ИЗ
    Справочник.ПодразделенияОрганизаций КАК Подразделения

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ШтатноеРасписание.Ссылка КАК ЭлементСхемы,
    ШтатноеРасписание.Подразделение КАК Родитель,
    "Позиция" КАК ТипУзла
ИЗ
    Справочник.ШтатноеРасписание КАК ШтатноеРасписание

Обратите внимание на логику ветвления в поле Родитель для подразделений. Если у подразделения нет вышестоящего подразделения (оно находится на самом верхнем уровне), то его родителем объявляется владелец — Организация. У позиций штатного расписания родителем всегда будет являться подразделение, к которому привязана позиция. У самой организации родитель отсутствует (равен НЕОПРЕДЕЛЕНО), что делает её корнем нашего дерева.

---

Шаг 2: Настройка собственной (произвольной) иерархии в СКД

Теперь нам нужно объяснить СКД, как эту плоскую таблицу развернуть в иерархическое дерево. Для этого используется механизм связей наборов данных. Мы свяжем наш набор данных сам с собой рекурсивным способом.

Если вы настраиваете схему в конструкторе СКД, перейдите на вкладку «Связи наборов данных» и добавьте строку со следующими параметрами:

  1. Источник связи: Наш набор данных (например, НаборДанныхИерархия).
  2. Приемник связи: Он же (НаборДанныхИерархия).
  3. Выражение-источник: Родитель.
  4. Выражение-приемник: ЭлементСхемы.
  5. Начальное значение связи: Здесь мы указываем отправную точку рекурсии. В нашем случае корнем являются организации, поэтому мы можем передать пустую ссылку организации или НЕОПРЕДЕЛЕНО.

В случае, когда схема компоновки данных формируется или дорабатывается программно (как это часто бывает в типовых конфигурациях вроде ЗУП), мы можем описать эту связь непосредственно в коде. Посмотрим на пример кода добавления иерархической связи:


// Создаем программную связь набора данных с самим собой для построения иерархии
Связь = СхемаКомпоновкиДанных.СвязиНаборовДанных.Добавить();
Связь.ИсточникСвязи = "НаборДанныхИерархия";
Связь.ПриемникСвязи = "НаборДанныхИерархия";
Связь.ВыражениеИсточник = "Родитель";
Связь.ВыражениеПриемник = "ЭлементСхемы";
Связь.Параметр = "ИерархияЭлемент";
Связь.НачальноеЗначениеСвязи = ПредопределенноеЗначение("Справочник.Организации.ПустаяСсылка");

После настройки связей обязательно перейдите в настройки варианта отчета в СКД. Вместо обычной группировки по полю ЭлементСхемы добавьте группировку с типом «Иерархия». Именно эта настройка заставит компоновщик использовать иерархические связи при выводе.

---

Шаг 3: Программный вывод результата СКД в ДеревоЗначений

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

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


// Инициализируем компоновщик макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;

// Важно: используем генератор макета для коллекции значений!
МакетКомпоновки = КомпоновщикМакета.Выполнить(
    СхемаКомпоновкиДанных, 
    КомпоновщикНастроек.Настройки, 
    ДанныеРасшифровки, , 
    Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений")
);

// Инициализируем процессор компоновки данных
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);

// Создаем пустое дерево значений, куда будет осуществлен вывод
ДеревоРезультат = Новый ДеревоЗначений;

// Инициализируем процессор вывода в коллекцию значений
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоРезультат);

// Запускаем процесс генерации дерева
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

// На выходе получаем готовое ДеревоЗначений с полностью построенной иерархией!

Благодаря использованию класса ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений, платформа самостоятельно создаст необходимые строки дерева, подчинит их друг другу и перенесет все выбранные ресурсы и реквизиты в соответствующие колонки.

---

Шаг 4: Подводные камни и важные нюансы оптимизации

Хотя этот механизм работает невероятно эффективно, существует несколько подводных камней, о которых важно помнить при промышленной разработке:

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

← На главную