При разработке подсистем хранения настроек, матриц цен, скидок или прав доступа часто возникает типовая задача: в исходной таблице (например, во временной таблице или регистре сведений) правила заданы для родительских групп иерархического справочника, а в результате требуется получить плоский список, где эти настройки распространены на все дочерние элементы и подпапки. Разберем особенности работы языка запросов 1С с иерархией и рассмотрим эффективные варианты реализации такой логики.
Первое, что приходит на ум разработчику — связать таблицу настроек со справочником через левое соединение с условием иерархической подчиненности. Однако в языке запросов платформы 1С:Предприятие конструкция В ИЕРАРХИИ допустима только в секциях фильтрации ГДЕ или ИМЕЮЩИЕ. Использовать конструкцию вида ПО Номенклатура.Ссылка В ИЕРАРХИИ (втНастройки.Группа) синтаксис не позволяет.
Кроме того, если передавать в секцию ГДЕ большой массив групп через В ИЕРАРХИИ (&МассивГрупп), платформа на уровне СУБД выполняет разворачивание узлов во временную таблицу. При большом объеме данных и глубоком дереве это приводит к заметной нагрузке на сервер. Разберем, какими способами можно обойти данное ограничение.
Если максимальная глубина вложенности справочника заранее ограничена и невелика (например, не превышает 5–10 уровней), наиболее быстрым и простым способом будет явное сопоставление поля группы со ссылкой элемента или его вышестоящими родителями через оператор ИЛИ.
Рассмотрим пример запроса, где к таблице переданных настроек присоединяются элементы номенклатуры:
ВЫБРАТЬ
тНастроек.ГруппаНоменклатуры КАК ГруппаНоменклатуры,
тНастроек.Цвет КАК Цвет,
тНастроек.Серия КАК Серия
ПОМЕСТИТЬ втНастройки
ИЗ
&ТаблицаНастроек КАК тНастроек
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втНастройки.ГруппаНоменклатуры КАК ГруппаНоменклатуры,
втНастройки.Цвет КАК Цвет,
втНастройки.Серия КАК Серия,
Номенклатура.Ссылка КАК Ссылка
ИЗ
втНастройки КАК втНастройки
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
ПО (втНастройки.ГруппаНоменклатуры = Номенклатура.Ссылка
ИЛИ втНастройки.ГруппаНоменклатуры = Номенклатура.Родитель
ИЛИ втНастройки.ГруппаНоменклатуры = Номенклатура.Родитель.Родитель
ИЛИ втНастройки.ГруппаНоменклатуры = Номенклатура.Родитель.Родитель.Родитель
ИЛИ втНастройки.ГруппаНоменклатуры = Номенклатура.Родитель.Родитель.Родитель.Родитель
ИЛИ втНастройки.ГруппаНоменклатуры = Номенклатура.Родитель.Родитель.Родитель.Родитель.Родитель)
И (втНастройки.Цвет = Номенклатура.Цвет)
И (втНастройки.Серия = Номенклатура.Серия)
Обратите внимание: Каждое обращение через точку (Родитель.Родитель) платформа транслирует в неявное соединение (LEFT OUTER JOIN) с таблицей этого же справочника на уровне СУБД. При слишком большом количестве уровней план выполнения может деградировать, поэтому для глубоких деревьев лучше применять другие подходы.
Если структура иерархии произвольная, а использовать циклы на встроенном языке не хочется, можно собрать таблицу связей всех предков и потомков за несколько шагов удвоения глубины. Для этого формируется временная таблица прямых родителей, которая затем соединяется сама с собой.
(Ссылка, Родитель) для всех непустых элементов.ОБЪЕДИНИТЬ) соединяем полученные связи по условию Текущий.Родитель = Следующий.Ссылка. На каждом шаге глубина разворота дерева удваивается (2, 4, 8, 16 уровней).(Элемент, Предок), с которой можно выполнять стандартное точное соединение ПО ТаблицаСвязей.Предок = втНастройки.Группа.Такой подход обеспечивает высокую скорость на стороне СУБД и не требует жесткого ограничения на число уровней иерархии.
Система компоновки данных (СКД) содержит встроенные оптимизированные алгоритмы обхода деревьев. Если задача решается в рамках формирования отчета или выгрузки данных, мы можем использовать схему компоновки программно:
ПроцессорКомпоновкиДанных и выгружаем результат в ТаблицаЗначений. Платформа сама корректно рассчитает подчиненность элементов без написания сложных запросов.Если доступ к конфигурации открыт и подобные запросы выполняются часто, наиболее производительное архитектурное решение — хранить развернутую иерархию в служебном непериодическом регистре сведений:
Элемент (СправочникСсылка), Родитель (СправочникСсылка), Уровень (Число).ПриЗаписи справочника заполняем регистр всеми вышестоящими родителями записываемого объекта.втНастройки.ГруппаНоменклатуры = РегистрИерархии.Родитель, получая сразу все подчиненные элементы без использования условий по цепочкам точек.Выбор конкретного способа зависит от возможности модификации метаданных конфигурации, предельной глубины иерархии и требований к быстродействию системы.