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

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

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

Способ 1: Использование запроса с итогами (фиксированная вложенность)

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

Рассмотрим этот метод по шагам:

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

Пример реализации:


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

Запрос.УстановитьПараметр("ТЗ", ИсходнаяТаблица);
РезультатЗапроса = Запрос.Выполнить();

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

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

Способ 2: Программная рекурсия на основе идентификаторов (ИД и Родитель)

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

Реализация функции преобразования:


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

Обратите внимание на важный нюанс: метод Найти() с параметром Истина выполняет поиск по всей глубине дерева. На больших объемах данных (десятки тысяч строк) этот метод будет замедляться. Чтобы оптимизировать процесс, рекомендуется использовать объект Соответствие (Map) для хранения ссылок на уже созданные строки дерева по их ключам.

Способ 3: Использование СКД (Система компоновки данных)

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

Разберем алгоритм работы с СКД по шагам:

  1. Создаем схему компоновки данных программно или в макете.
  2. Добавляем набор данных - таблицу.
  3. В разделе "Связи наборов данных" указываем связь набора с самим собой. Поле источника — ИД, поле приемника — РодительИД.
  4. Настраиваем структуру отчета: добавляем группировку по полю ИД с типом "Иерархия".
  5. Программно инициализируем процессор компоновки и выгружаем результат в ДеревоЗначений.

Этот метод является наиболее универсальным, так как позволяет легко настраивать сортировки, отборы и итоговые показатели без переписывания основного кода преобразования.

Способ 4: Оптимизированный обход через "Соответствие"

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

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


// Создаем дерево и соответствие для быстрого доступа к узлам
УзлыДерева = Новый Соответствие;

Для Каждого СтрокаТЗ Из ИсходнаяТаблица Цикл
    НоваяСтрока = Неопределено;
    Родитель = УзлыДерева[СтрокаТЗ.РодительИД];
    
    Если Родитель = Неопределено Тогда
        НоваяСтрока = Дерево.Строки.Добавить();
    Иначе
        НоваяСтрока = Родитель.Строки.Добавить();
    КонецЕсли;
    
    ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗ);
    УзлыДерева.Вставить(СтрокаТЗ.ИД, НоваяСтрока);
КонецЦикла;

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

Важные замечания по типам данных

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

Выясним причину, почему дерево может не отображаться в интерфейсе: если вы работаете в Управляемых формах, помните, что ДеревоЗначений — это объект, который не может существовать на клиенте напрямую. Вам необходимо преобразовать его в ДанныеФормыДерево с помощью метода ЗначениеВРеквизитФормы, чтобы пользователь увидел результат на экране.

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

← На главную