К статье
Практический материал

Разработка и Конфигурация · 12 мин

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

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

Дерево значений в 1С удобно для иерархического отображения данных, но плохо подходит для выгрузки, выгрузки в файл, поиска и обмена с внешними системами. Таблица значений, наоборот, легко сериализуется, выгружается в Excel и передаётся через веб-сервисы. Поэтому задача «превратить дерево в плоскую таблицу» возникает регулярно: при формировании отчётов, обмене данными, печати иерархических справочников.

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

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

Когда нужна выгрузка дерева в таблицу

Дерево значений чаще всего появляется при обходе иерархических справочников, результатов запросов с итогами по группировкам (ОБЩИЕ ИТОГИ, ИТОГИ ПО) и при работе с формами, где данные показаны в виде дерева. Плоская таблица нужна в следующих ситуациях:

  • выгрузка данных в Excel, CSV или JSON, где иерархию нужно представить плоско;
  • передача данных через COM-соединение, HTTP- или веб-сервисы;
  • поиск и фильтрация по всем строкам без учёта вложенности;
  • загрузка данных в другую базу, где иерархия восстанавливается по полю родителя;
  • печатные формы и отчёты, где проще идти по линейной коллекции.

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

Базовый алгоритм: рекурсивный обход дерева

Самый читаемый способ — рекурсия. Создаём таблицу значений, копируем в неё состав колонок дерева и обходим строки: для каждой строки добавляем строку в таблицу, заполняем значения и вызываем ту же процедуру для подчинённых строк.

Функция ДеревоВТаблицу(Дерево) Экспорт

Таблица = Новый ТаблицаЗначений;

Для Каждого Колонка Из Дерево.Колонки Цикл

Таблица.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);

КонецЦикла;

// Служебные колонки для сохранения иерархии

Таблица.Колонки.Добавить("Уровень", Новый ОписаниеТипов("Число"));

Таблица.Колонки.Добавить("Родитель");

ОбойтиСтроки(Дерево.Строки, Таблица, 0, Неопределено);

Возврат Таблица;

КонецФункции

Процедура ОбойтиСтроки(СтрокиДерева, Таблица, Уровень, Родитель)

Для Каждого СтрокаДерева Из СтрокиДерева Цикл

НоваяСтрока = Таблица.Добавить();

ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДерева);

НоваяСтрока.Уровень = Уровень;

НоваяСтрока.Родитель = Родитель;

// Ссылка на текущую строку как родителя для вложенных

ОбойтиСтроки(СтрокаДерева.Строки, Таблица, Уровень + 1,

СтрокаДерева.Ссылка);

КонецЦикла;

КонецПроцедуры

Обратите внимание: колонка «Родитель» в примере заполняется ссылкой из колонки «Ссылка» дерева — это работает, если дерево построено по иерархическому справочнику. Если дерева такой колонки нет, вместо ссылки используйте порядковый номер строки в таблице (см. раздел про иерархию ниже).

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

Итеративный вариант без рекурсии

Рекурсия удобна, но у неё есть ограничение: при очень глубокой вложенности (сотни уровней) возможно переполнение стека вызовов. На практике такие деревья редки, но для универсального кода в общем модуле надёжнее итеративный обход со стеком.

Функция ДеревоВТаблицуИтеративно(Дерево) Экспорт

Таблица = Новый ТаблицаЗначений;

Для Каждого Колонка Из Дерево.Колонки Цикл

Таблица.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);

КонецЦикла;

Таблица.Колонки.Добавить("Уровень", Новый ОписаниеТипов("Число"));

Стек = Новый Массив;

Для Каждого СтрокаДерева Из Дерево.Строки Цикл

Стек.Добавить(Новый Структура("Строка,Уровень", СтрокаДерева, 0));

КонецЦикла;

Пока Стек.Количество() > 0 Цикл

Элемент = Стек[Стек.Количество() - 1];

Стек.Удалить(Стек.Количество() - 1);

НоваяСтрока = Таблица.Добавить();

ЗаполнитьЗначенияСвойств(НоваяСтрока, Элемент.Строка);

НоваяСтрока.Уровень = Элемент.Уровень;

Для Каждого Подчиненная Из Элемент.Строка.Строки Цикл

Стек.Добавить(Новый Структура("Строка,Уровень",

Подчиненная, Элемент.Уровень + 1));

КонецЦикла;

КонецЦикла;

Возврат Таблица;

КонецФункции

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

Сравнение вариантов

Рекурсивный обход

Код короче и понятнее, естественный порядок строк. Ограничение — риск переполнения стека при экстремально глубокой иерархии.

Итеративный обход со стеком

Без ограничения по глубине, подходит для универсальных библиотек. Минус — сложнее код и нарушенный порядок строк без дополнительной сортировки.

Запрос с ИТОГИ ПО + обход выборки

Если дерево ещё не построено, иногда проще сразу получить плоскую выборку запросом без группировок, а иерархию хранить полем Родитель.

Как сохранить иерархию в плоской таблице

Главный вопрос преобразования — как потом восстановить дерево или отфильтровать строки по ветке. Есть три рабочих способа.

Колонка «Родитель» со ссылкой

Если дерево построено по иерархическому справочнику, в нём почти всегда есть колонка со ссылкой на элемент. Тогда в таблицу пишется ссылка на родительский элемент — это самый надёжный вариант, поскольку иерархия восстанавливается одним запросом или поиском.

Связь по индексу строки

Если ссылок нет (дерево произвольное, например результат произвольной группировки), добавьте в таблицу колонку «КлючСтроки» — порядковый номер добавленной строки — и колонку «КлючРодителя». При обходе запоминайте ключ текущей строки и передавайте его вниз как ключ родителя для подчинённых.

Только уровень вложенности

Если восстанавливать иерархию не нужно, а требуется лишь визуальный отступ при выгрузке в Excel или печать, достаточно одной колонки «Уровень». По ней легко сделать отступы пробелами в текстовом представлении строки.

Частный случай: дерево на форме и построитель отчёта

Если дерево находится в реквизите формы типа «ДеревоЗначений», обход ничем не отличается — работайте с реквизитом как с обычным деревом. На клиенте рекурсия тоже доступна, но при больших объёмах обход лучше выполнять на сервере: передавать само дерево между клиентом и сервером нельзя напрямую, поэтому дерево обычно хранится в реквизите формы и обрабатывается в серверной процедуре «НаСервере».

Если дерево получено из результата запроса через РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией), подумайте, не проще ли сразу получить плоскую выборку: тот же запрос без предложения ИТОГИ вернёт линейный результат, а связь с родителем уже есть в данных справочника. Это избавит от обхода вовсе.

Универсальную функцию преобразования удобно разместить в общем серверном модуле с признаком «Вызов сервера». Тогда её можно вызывать и из отчётов, и из обработок обмена, не дублируя код. Параметром функции полезно сделать имя колонки со ссылкой, чтобы функция работала с разными деревьями.
ПЗ
Практическое замечание

Проверка результата и типичные ошибки

После выгрузки убедитесь, что таблица заполнена корректно. Самая простая проверка — сравнить количество строк: число строк таблицы должно равняться общему числу строк дерева на всех уровнях. Для контроля можно заранее подсчитать строки дерева аналогичным обходом или сверить итоги по ключевым суммовым колонкам (с учётом того, что групповые строки дерева часто дублируют итоги подчинённых).

Также проверьте, что у корневых строк колонка «Родитель» пуста, а у подчинённых — заполнена, и что уровни начинаются с нуля. Если в таблице все строки оказались на одном уровне, скорее всего, рекурсивный вызов для подчинённых строк не выполняется или уровень не увеличивается.

Типичные ошибки
01
Обход только Дерево.Строки без рекурсии — в таблицу попадают лишь корневые строки, вся вложенность теряется
02
Использование ЗаполнитьЗначенияСвойств при несовпадающих типах колонок — значения приводятся к типу колонки таблицы, данные могут обрезаться или очиститься
03
Добавление колонок таблицы без типов — колонка получает произвольный тип, что мешает последующей выгрузке в Excel и сортировке
04
Игнорирование строк-итогов группировок — в плоской таблице итоги групп дублируют суммы подчинённых строк, что искажает сводные показатели
05
Передача дерева значений как параметра на сервер — дерево нельзя сериализовать напрямую, код падает с ошибкой передачи данных
Контрольные точки

Как проверить результат

Количество строк таблицы
Норма

Равно общему числу строк дерева на всех уровнях

Тревожный признак

Строк меньше — обход не зашёл во вложенные коллекции Строки

Колонка Уровень
Норма

Корневые строки имеют уровень 0, вложенные — больше

Тревожный признак

Все строки с одним уровнем — рекурсия не работает

Колонка Родитель
Норма

У корневых строк пусто, у подчинённых заполнено

Тревожный признак

Везде пусто — родитель не передаётся в рекурсивный вызов

Значения колонок
Норма

Данные совпадают с деревом, типы сохранены

Тревожный признак

Пустые или обрезанные значения — несовпадение имён или типов колонок

Ограничения и риски

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

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

FAQ

Итог

Что важно запомнить

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

Есть ли в 1С готовый метод выгрузки дерева значений в таблицу?

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

Как выгрузить дерево в таблицу, если нет колонки со ссылкой на справочник?

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

Можно ли выполнить обход дерева на клиенте тонкого клиента?

Да, рекурсивный обход и работа с таблицей значений доступны на клиенте, если оба объекта являются реквизитами формы. Но при большом объёме данных обход лучше делать на сервере — он выполняется заметно быстрее.

Почему после ЗаполнитьЗначенияСвойств часть колонок таблицы пустая?

Метод копирует значения только для колонок с одинаковыми именами в источнике и приёмнике. Проверьте, что имена колонок таблицы точно совпадают с именами колонок дерева, включая регистр букв.

Как выгрузить дерево в Excel через таблицу значений?

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

Что делать со строками итогов группировок при выгрузке?

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