При разработке конфигураций на платформе 1С 8.3 программисты часто сталкиваются с необходимостью отображения данных в виде иерархии. Если для временного отображения идеально подходит объект ДеревоЗначений, то вопрос постоянного хранения такой структуры в базе данных требует более детального анализа. Рассмотрим основные способы реализации деревьев, их преимущества и недостатки, а также разберем алгоритмы оптимизации работы с ними.
Самый простой и очевидный способ — использование встроенного механизма платформы. Справочники в 1С поддерживают иерархию (типа «иерархия элементов» или «иерархия групп и элементов»). Это готовое дерево, где у каждого элемента есть реквизит Родитель.
Проанализируем этот подход. Платформа берет на себя все заботы по контролю целостности, исключению циклов (когда элемент становится родителем самого себя) и визуализации в списках. Однако у этого метода есть ограничения. Если дерево должно быть частью документа или данные очень динамичны, справочник может «раздуваться», что со временем приведет к падению производительности при поиске и записи.
Рассмотрим, как ограничить глубину дерева, если это необходимо бизнес-логике. Для этого в метаданных справочника мы можем программно контролировать свойство КоличествоУровнейИерархии. Также в запросах для работы с такими структурами мы будем использовать конструкцию В ИЕРАРХИИ, которая позволяет эффективно выбирать все дочерние узлы любой вложенности.
Если структура дерева нужна только в контексте конкретного объекта (например, настройки отчета или специфическое дерево в документе) и по его узлам не требуется выполнять поиск через запросы, мы можем использовать сериализацию. Разберем этот алгоритм подробнее:
ДеревоЗначений на сервере.ХранилищеЗначения.Посмотрим на пример кода для записи:
// Предположим, ДеревоОбъект — это наше заполненное ДеревоЗначений
НоваяЗапись = Справочники.НастройкиДеревьев.СоздатьЭлемент();
НоваяЗапись.Наименование = "Моя структура";
НоваяЗапись.ДанныеДерева = Новый ХранилищеЗначения(ДеревоОбъект);
НоваяЗапись.Записать();
И пример для чтения:
Запись = Справочники.НастройкиДеревьев.НайтиПоНаименованию("Моя структура");
Если Не Запись.Пустая() Тогда
ДеревоИзБазы = Запись.ДанныеДерева.Получить();
// Теперь ДеревоИзБазы можно вывести на форму
КонецЕсли;
Важный нюанс: Этот метод делает данные «невидимыми» для языка запросов. Вы не сможете посчитать сумму по ветке дерева, не считав весь объект целиком в память сервера. Используйте этот способ только для хранения настроек или компактных структур.
Для документов часто применяют метод «плоского» хранения в табличной части с программной сборкой в дерево при открытии формы. Выясним причину популярности этого метода: он позволяет хранить данные внутри документа, сохраняя возможность их анализа в запросах.
Разберем технологию реализации:
ИдентификаторУзла (тип Строка или УникальныйИдентификатор) и ИдентификаторРодителя.ПриЧтенииНаСервере) мы перебираем строки ТЧ и программно строим ДеревоЗначений, сопоставляя родителей и детей.Этот метод хорошо работает для небольших деревьев (2-3 уровня вложенности). Если же уровней много, процесс сборки/разборки может стать ресурсоемким.
Для высоконагруженных систем, где чтение дерева происходит в тысячи раз чаще, чем его изменение, мы рекомендуем рассмотреть алгоритмы из области классических БД. В 1С их удобнее всего реализовывать на базе Регистров Сведений.
Materialized Path (Материализованный путь): в каждой записи регистра хранится строка вида 001.005.012. Это полный путь от корня. Проанализируем преимущество: чтобы найти всех потомков узла 001.005, нам достаточно выполнить простой запрос с условием ПОДОБНО "001.005.%". Это работает молниеносно даже на миллионах записей.
Nested Sets (Вложенные множества): каждому узлу присваиваются два числа — левый и правый ключ. Все потомки узла имеют ключи, находящиеся в диапазоне между левым и правым ключом родителя. Это позволяет получать целые ветви одним запросом без рекурсии, но значительно усложняет вставку новых элементов, так как требует пересчета ключей у «соседей».
Если ваше дерево содержит тысячи элементов, попытка загрузить его целиком на управляемую форму приведет к зависанию интерфейса. Рассмотрим, как реализовать динамическую подгрузку данных (Lazy Loading):
ПриРазворачивании проверяем, загружены ли данные. Если нет — удаляем «пустышку», запрашиваем данные из базы только для этого конкретного узла и добавляем их в дерево.Посмотрим на пример обработки разворачивания:
&НаСервере
Процедура ПодгрузитьПодчиненныеУзлы(ИдентификаторСтроки)
ЭлементДерева = ДанныеФормыДерево.НайтиПоИдентификатору(ИдентификаторСтроки);
// Логика запроса к БД для получения детей
// ...
// Заполнение ЭлементДерева.ПолучитьЭлементы()
КонецПроцедуры
В современных версиях 1С 8.3 (начиная с 8.3.6) появилась отличная поддержка JSON. Если структура вашего дерева постоянно меняется (добавляются новые свойства узлов), хранение дерева в виде JSON-строки в реквизите Строка (неограниченная) может стать отличной альтернативой ХранилищуЗначения.
Преимущества JSON:
Подводя итог, выбор метода зависит от ваших задач. Если нужна простота и стандартные отчеты — выбирайте иерархические справочники. Если дерево является частью документа — используйте табличные части. Если же вы строите сложную аналитическую систему с огромными деревьями — смотрите в сторону регистров сведений с алгоритмом материализованных путей.