Как превратить таблицу значений в дерево значений в 1С

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

В процессе разработки на платформе 1С:Предприятие часто возникает задача визуализации данных в иерархическом виде. Исходные данные обычно приходят в виде «плоской» структуры — объекта ТаблицаЗначений, но для удобства пользователя их требуется представить в виде объекта ДеревоЗначений. Типичный пример — группировка документов или записей по датам, контрагентам или любым другим аналитическим разрезам. Разберем наиболее эффективные способы решения этой задачи, начиная от простых запросов и заканчивая использованием универсальных механизмов платформы.

Метод 1. Использование Построителя Запроса

Этот способ является одним из самых лаконичных и быстрых, особенно если работа ведется в обычных формах. Объект ПостроительЗапроса позволяет использовать таблицу значений в качестве источника данных и автоматически формирует иерархию на основе указанных измерений. Рассмотрим программную реализацию этого метода. Проанализируем код функции, которая принимает таблицу и имя колонки для группировки.


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

В данном примере мы используем свойство Измерение. Когда мы помечаем колонку как измерение, ПостроительЗапроса понимает, что по этому полю необходимо создать уровень вложенности. Метод Выгрузить с параметром ОбходРезультатаЗапроса.ПоГруппировкамСИерархией превращает плоский результат в полноценное ДеревоЗначений.

Метод 2. Классический запрос с использованием временных таблиц

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

  1. Создадим объект МенеджерВременныхТаблиц.
  2. Поместим исходную ТаблицаЗначений во временную таблицу внутри запроса.
  3. Выполним выборку с использованием предложения ИТОГИ ПО.

Посмотрим на пример реализации:


// Предположим, у нас есть ТЗ с колонками "Дата" и "Сумма"
МенеджерВТ = Новый МенеджерВременныхТаблиц;

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = 
    "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ВнешняяТЗ КАК ВнешняяТЗ;
    |ВЫБРАТЬ * ИЗ ВТ_Данные КАК Данные
    |ИТОГИ СУММА(Сумма) ПО Дата";

Запрос.УстановитьПараметр("ВнешняяТЗ", ТЗ);

РезультатЗапроса = Запрос.Выполнить();
// Выгружаем в дерево значений
Дерево = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);

Этот метод считается наиболее производительным при больших объемах данных, так как механизмы СУБД (или внутреннего движка 1С для временных таблиц) оптимизированы для операций группировки и агрегации. Важно помнить, что при использовании ИТОГИ в дереве автоматически создаются строки-родители, содержащие суммовые показатели по подчиненным строкам.

Метод 3. Ручной перебор и метод НайтиСтроки

Иногда использование запросов избыточно или невозможно (например, в специфических условиях работы с типами, которые плохо сериализуются для запроса). В таких случаях мы можем сформировать ДеревоЗначений программно, используя цикл и метод НайтиСтроки(). Разберем этот алгоритм подробнее.

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


// Копируем структуру колонок
Дерево = Новый ДеревоЗначений;
Для Каждого Колонка Из ТЗ.Колонки Цикл
    Дерево.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
КонецЦикла;

// Получаем уникальные даты для создания узлов верхнего уровня
ТаблицаДат = ТЗ.Скопировать();
ТаблицаДат.Свернуть("Дата");

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

Важный момент: Для ускорения этого процесса рекомендуется предварительно отсортировать исходную таблицу по колонке группировки. Это значительно повышает скорость работы метода НайтиСтроки() на больших массивах данных.

Метод 4. Система компоновки данных (СКД)

Если задача стоит в рамках разработки отчета или сложной формы, стоит рассмотреть программный вызов СКД. Этот метод дает максимальную гибкость: вы можете легко настраивать иерархию любой вложенности (например, Год -> Месяц -> День), не переписывая программный код. Мы можем создать схему в памяти, указать Объект как источник данных и вывести результат в дерево.

Проанализируем преимущества использования СКД:

Особенности для Управляемых форм

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

  1. На сервере формируется объект ДеревоЗначений одним из вышеперечисленных способов.
  2. С помощью метода ЗначениеВДанныеФормы() данные передаются в реквизит формы для отображения.

Посмотрим на пример кода в контексте серверной процедуры формы:


&НаСервере
Процедура ЗаполнитьДеревоНаФорме()
    // Предположим, ТЗ_Данные уже заполнена
    ДеревоОбъект = ПреобразоватьТЗвДеревоЧерезПостроитель(ТЗ_Данные, "Дата");
    
    // Реквизит формы называется "ДеревоНаФорме"
    ЗначениеВДанныеФормы(ДеревоОбъект, ДеревоНаФорме);
КонецПроцедуры

Оптимизация и производительность

При работе с деревьями значений важно помнить об объеме оперативной памяти. В отличие от плоской таблицы, каждая строка дерева является объектом коллекции, что увеличивает накладные расходы памяти. Рассмотрим несколько советов по оптимизации:

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

← На главную