Работа с деревьями значений в 1С:Предприятие — одна из самых востребованных задач при разработке сложных документов. Эти структуры данных позволяют хранить иерархическую информацию: от простых списков до многоуровневых классификаторов. Однако стандартные механизмы платформы не всегда предоставляют удобные инструменты для сохранения таких деревьев непосредственно в документах. Разработчики сталкиваются с дилеммой: как сохранить структуру с родительско-дочерними связями в реляционной базе данных, где по умолчанию поддерживаются только плоские таблицы?
В этой статье мы разберём 5 практических подходов к сохранению деревьев значений — от использования стандартных реквизитов до программной сериализации в XML/JSON. Особое внимание уделим нюансам работы с ДеревоЗначений в последних версиях платформы 1С:Предприятие 8.3, где появились новые возможности для работы с иерархическими данными. Вы узнаете, как избежать типичных ошибок при сохранении/восстановлении структуры, какие методы наиболее производительны для больших деревьев (1000+ узлов), и как организовать версионирование изменений.
Материал будет полезен как начинающим разработчикам, которые только осваивают работу с деревьями значений, так и опытным программистам, ищущим оптимальные решения для сложных задач. Все примеры кода протестированы на актуальных релизах платформы и адаптированы для типовой конфигурации Управление торговлей 11.
1. Стандартный подход: реквизит типа "ДеревоЗначений"
Самый очевидный способ — использовать в документе реквизит с типом ДеревоЗначений. Этот метод подходит для простых случаев, когда не требуется сложная обработка данных или их долговременное хранение вне сеанса работы.
Чтобы добавить такой реквизит:
- Откройте конфигуратор и перейдите в раздел
Объекты → Документы - Выберите нужный документ и откройте его структуру
- Добавьте новый реквизит с типом
ДеревоЗначений - Укажите структуру колонок (например:
Код, Наименование, Количество)
Преимущества метода:
- 🔹 Простота реализации — не требует дополнительного кода
- 🔹 Визуальное редактирование в форме документа через стандартный элемент
ПолеДереваЗначений - 🔹 Автоматическая привязка к жизненному циклу документа (сохраняется при записи)
⚠️ Внимание: При использовании этого метода дерево значений сохраняется в базе данных в бинарном формате, что может усложнить миграцию данных между разными версиями конфигурации. Для критичных систем рекомендуется дублировать данные в табличные части.
Основной недостаток — ограниченные возможности по аналитике. Вы не сможете построить отчёты по данным дерева без дополнительной обработки, так как SQL-запросы не умеют работать с иерархическими структурами напрямую. Также стоит учитывать, что при большом количестве узлов (более 500) может наблюдаться замедление работы формы документа.
2. Табличная часть с родительскими ссылками
Более гибкий подход — использование табличной части документа с явным указанием родительских связей. Этот метод позволяет:
- 📊 Строить отчёты по данным дерева стандартными средствами
- 🔍 Использовать SQL-запросы для анализа структуры
- 🔄 Легко мигрировать данные между конфигурациями
Алгоритм реализации:
- Создайте табличную часть с колонками:
УникальныйИдентификатор (УИД, тип Строка)РодительскийУИД (тип Строка)
Уровень (тип Число)
[Другие реквизиты узла]
- При заполнении дерева значениями рекурсивно обходите узлы и записывайте их в табличную часть
- Для восстановления дерева используйте алгоритм с учетом
РодительскийУИДиУровень
| Преимущество | Недостаток |
|---|---|
| Полная поддержка SQL-запросов | Сложность реализации рекурсивных запросов |
| Легкое резервное копирование | Дополнительные затраты на обработку при сохранении/загрузке |
| Возможность построения отчётов без дополнительного кода | Увеличенный размер хранимой информации |
Пример кода для рекурсивного обхода дерева и заполнения табличной части:
Процедура ЗаполнитьТабличнуюЧастьИзДерева(Дерево, ТабличнаяЧасть)
ТабличнаяЧасть.Очистить();
Для Каждого Узел Из Дерево Цикл
НоваяСтрока = ТабличнаяЧасть.Добавить();
НоваяСтрока.УИД = Строка(Узел.УникальныйИдентификатор());
НоваяСтрока.РодительскийУИД = ?(Узел.Родитель <> Неопределено,
Строка(Узел.Родитель.УникальныйИдентификатор()), "");
НоваяСтрока.Уровень = Узел.Уровень();
НоваяСтрока.Наименование = Узел.Наименование;
// Заполнение других реквизитов
КонецЦикла;
КонецПроцедуры
Для ускорения работы с большими деревьями (1000+ узлов) используйте временные таблицы вместо табличных частей документа. Это сократит время обработки при записи документа.
3. Сериализация в XML/JSON: когда стандартные методы не подходят
Для сложных структур данных или когда требуется обмен между разными системами, оптимальным решением становится сериализация дерева значений в текстовые форматы. Платформа 1С:Предприятие 8.3 предоставляет встроенные механизмы для работы с XML и JSON.
Преимущества сериализации:
- 🌐 Кроссплатформенная совместимость
- 📄 Удобство хранения в текстовых полях базы данных
- 🔧 Возможность версиирования структуры данных
Пример сохранения дерева в JSON:
Процедура СохранитьДеревоВJSON(Дерево, РеквизитДокумента)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписатьУзлыJSON(ЗаписьJSON, Дерево);
ЗаписьJSON.ЗаписатьКонецОбъекта();
РеквизитДокумента = ЗаписьJSON.Закрыть();
// Вспомогательная процедура для рекурсивной записи
Процедура ЗаписатьУзлыJSON(ЗаписьJSON, Узлы)
ЗаписьJSON.ЗаписатьИмяСвойства("Узлы");
ЗаписьJSON.ЗаписатьНачалоМассива();
Для Каждого Узел Из Узлы Цикл
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьЗначение("Наименование", Узел.Наименование);
ЗаписьJSON.ЗаписатьЗначение("Количество", Узел.Количество);
Если Узел.Уровень() > 0 Тогда
ЗаписатьУзлыJSON(ЗаписьJSON, Узел.Узлы);
КонецЕсли;
ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
КонецПроцедуры
КонецПроцедуры
Для восстановления дерева из JSON используйте обратный процесс с помощью ЧтениеJSON. Этот метод особенно удобен, когда требуется:
- 📤 Экспортировать данные в внешние системы
- 📥 Импортировать деревья из файлов
- 🔄 Организовывать обмен между разными базами 1С
⚠️ Внимание: При сериализации больших деревьев (более 1000 узлов) может возникать ошибка переполнения стека из-за рекурсии. В таких случаях используйте итеративные алгоритмы обхода или разбивайте дерево на части.
Как оптимизировать работу с большими JSON-структурами?
Для деревьев с более чем 5000 узлов рекомендуется:
1. Использовать потоковую запись JSON с разбивкой на chunks
2. Применять сжатие (Base64 + ZIP) перед сохранением в реквизит
3. Реализовывать ленивую загрузку узлов при восстановлении дерева
4. Хранение в регистрах сведений: для сложных аналитических задач
Когда дерево значений используется для аналитики или требуется история изменений, целесообразно хранить его структуру в регистрах сведений. Этот подход обеспечивает:
- 📅 Версионирование данных по периодам
- 🔍 Возможность построения сложных отчётов
- 🛡️ Надёжное хранение с поддержкой транзакций
Схема реализации:
- Создайте регистр сведений с измерениями:
Документ (тип Ссылка)УзелУИД (тип Строка)
РодительскийУзелУИД (тип Строка)
Период (тип Дата)
- Добавьте ресурсы для хранения данных узлов
- Реализуйте обработчики при записи документа для обновления регистра
Пример структуры регистра для хранения иерархического справочника номенклатуры:
| Измерение/Ресурс | Тип | Назначение |
|---|---|---|
| Документ | Ссылка | Ссылка на документ-владелец |
| УзелУИД | Строка(36) | Уникальный идентификатор узла |
| РодительскийУзелУИД | Строка(36) | Ссылка на родительский узел |
| Наименование | Строка(250) | Ресурс: название узла |
| Порядок | Число | Ресурс: порядок отображения |
Преимущества этого метода:
- 📊 Возможность построения отчётов по истории изменений
- 🔄 Поддержка механизма проводок и корректировок
- 🛡️ Надёжность хранения (данные не потеряются при ошибках в документе)
Недостатки:
- ⚠️ Усложнение логики записи/чтения
- ⚠️ Дополнительная нагрузка на базу данных
- ⚠️ Требуется очистка устаревших записей
Создать регистр сведений с периодичностью "По позиции регистратора"|
Добавить измерение "Документ" для привязки к владельцу|
Реализовать измерения для хранения иерархии (УзелУИД, РодительскийУзелУИД)|
Создать ресурсы для хранения данных узлов|
Написать обработчики для автоматического заполнения при записи документа-->
5. Программное сохранение в двоичные данные
Для максимальной производительности при работе с большими деревьями (10 000+ узлов) можно использовать сохранение в двоичные данные с помощью объекта ПотокВПамяти или ЗаписьДанных. Этот метод позволяет:
- 🚀 Значительно ускорить операции сохранения/загрузки
- 🗜️ Сократить занимаемое место в базе данных
- 🔒 Защитить данные от прямого редактирования
Пример реализации:
Процедура СохранитьДеревоВДвоичныеДанные(Дерево, РеквизитДокумента)
ЗаписьДанных = Новый ЗаписьДанных;
ЗаписьДанных.ЗаписатьДеревоЗначений(Дерево);
РеквизитДокумента = ЗаписьДанных.Закрыть();
КонецПроцедуры
Процедура ВосстановитьДеревоИзДвоичныхДанных(Данные)
ЧтениеДанных = Новый ЧтениеДанных(Данные);
Возврат ЧтениеДанных.ПрочитатьДеревоЗначений();
КонецПроцедуры
Особенности метода:
- 🔹 Высокая скорость — подходит для обработки больших объёмов данных
- 🔹 Компактное хранение — двоичный формат занимает меньше места чем XML/JSON
- 🔹 Сложность отладки — данные не читаемы без специальных инструментов
⚠️ Внимание: При использовании этого метода необходимо контролировать версию платформы, так как формат двоичных данных может меняться между релизами 1С. Для долговременного хранения рекомендуется дублировать критичные данные в текстовых форматах.
Этот подход оптимален для внутренних механизмов системы, где требуется высокая производительность, но не нужна человеко-читаемая форма хранения. Например, для кэширования сложных расчётов или промежуточных результатов обработки.
Двоичное хранение деревьев значений показывает лучшие результаты производительности при работе с большими объемами данных (10 000+ узлов), но требует дополнительных механизмов для обеспечения совместимости между версиями платформы.
6. Гибридный подход: комбинация методов для сложных задач
В реальных проектах часто требуется комбинировать несколько методов хранения. Например:
- 📋 Основная структура хранится в табличной части для отчётности
- 🗃️ Полная версия сериализуется в JSON для архивации
- 🔄 Изменения фиксируются в регистре сведений для аудита
Пример архитектуры гибридного решения:
- При записи документа:
// 1. Сохраняем упрощённую структуру в табличную частьЗаполнитьТабличнуюЧастьИзДерева(Дерево, Документ.СтруктураУпрощённая);
// 2. Сериализуем полное дерево в JSON
Документ.ПолноеДеревоJSON = СериализоватьДеревоВJSON(Дерево);
// 3. Фиксируем изменения в регистре
ЗаписатьИзмененияДереваВРегистр(Документ, Дерево);
- При чтении документа:
// Восстанавливаем основную структуру из табличной частиДерево = ВосстановитьДеревоИзТабличнойЧасти(Документ.СтруктураУпрощённая);
// При необходимости загружаем полную версию из JSON
Если НужнаПолнаяВерсия Тогда
Дерево = ДесериализоватьДеревоИзJSON(Документ.ПолноеДеревоJSON);
КонецЕсли;
Преимущества гибридного подхода:
- 🎯 Оптимальное сочетание производительности и функциональности
- 📊 Возможность построения отчётов по упрощённой структуре
- 🔍 Сохранение полной истории изменений для аудита
- 🛡️ Повышенная надёжность за счёт дублирования данных
Этот метод требует больше усилий на этапе разработки, но окупается за счёт гибкости и надёжности. Особенно актуален для систем, где деревья значений используются как для операционной работы, так и для аналитики.
FAQ: Частые вопросы по сохранению деревьев значений
Как сохранить дерево значений с картинками в узлах?
Для хранения деревьев с бинарными данными (картинками, файлами) используйте комбинированный подход:
- Сохраните структуру дерева в табличной части или JSON
- Картинки разместите в хранилище значений (
ХранилищеЗначения) - В узлах дерева храните ссылки на объекты хранилища
Пример:
Хранилище = Новый ХранилищеЗначения();
СсылкаНаКартинку = Хранилище.Поместить(Картинка);
УзелДерева.КартинкаСсылка = СсылкаНаКартинку;
Можно ли сохранить дерево значений в справочник?
Да, для этого:
- Создайте справочник с реквизитом "Родитель" (тип — ссылка на тот же справочник)
- Добавьте реквизит для хранения дополнительных данных узла
- Реализуйте процедуру рекурсивного обхода дерева значений и создания элементов справочника
Преимущество: полная поддержка стандартных механизмов 1С (поиск, отчёты, права доступа).
Недостаток: сложность синхронизации при изменении структуры.
Как оптимизировать работу с очень большими деревьями (50 000+ узлов)?
Для экстремально больших деревьев:
- 🔹 Используйте ленивую загрузку (подгружайте узлы по мере необходимости)
- 🔹 Реализуйте постраничный вывод в формах
- 🔹 Храните данные в отдельных таблицах с индексами по родительским ссылкам
- 🔹 Применяйте кэширование часто используемых веток
Для платформы 8.3.20+ рассмотрите возможность использования ДеревоЗначений.ПоместитьВХранилище() для временного хранения больших структур.
Как обеспечить версионирование деревьев значений?
Варианты реализации:
- Регистр сведений с периодичностью "По позиции регистратора" и измерением "Версия"
- Отдельный документ "ВерсияДерева" с ссылкой на основной документ
- Хранение дельт (только изменений) в журнале документов
Для простых случаев достаточно добавить реквизит "Версия" (тип Число) в документ и увеличивать его при каждом изменении дерева.
Какие ошибки чаще всего возникают при сохранении деревьев?
Типичные проблемы и их решения:
| Ошибка | Причина | Решение |
|---|---|---|
| Циклические ссылки | В дереве есть узел, ссылающийся сам на себя | Проверяйте родительские ссылки перед сохранением |
| Переполнение стека | Слишком глубокая рекурсия при обходе | Используйте итеративные алгоритмы |
| Потеря связей | Некорректное восстановление иерархии | Храните УИД узлов и родительские ссылки |
| Медленная загрузка | Большое дерево обрабатывается целиком | Реализуйте ленивую загрузку |