При разработке запросов и алгоритмов обработки данных в 1С часто возникает задача определить, входит ли подчиненный элемент в определенную группу или вышестоящее подразделение. Стандартная конструкция запроса В ИЕРАРХИИ отлично работает в направлении «сверху вниз», когда необходимо отобрать все дочерние узлы для заданной группы. Однако когда требуется проверить связь «снизу вверх» (от подчиненного узла к родителям) или связать две таблицы по признаку вхождения, разработчики нередко сталкиваются с трудностями и громоздкими конструкциями через обращение к свойству Родитель через точку.
Разберем детально, почему прямое обращение через цепочку родителей не всегда эффективно, и рассмотрим правильные варианты реализации этой задачи как на встроенном языке, так и в механизмах запросов и СКД.
Проанализируем распространенную ситуацию: разработчик пытается сравнить элемент с родителем, перебирая уровни вручную:
Работники.ПодразделениеОрганизации = ДанныеСпецодежды.ПодразделениеОрганизации
ИЛИ Работники.ПодразделениеОрганизации.Родитель = ДанныеСпецодежды.ПодразделениеОрганизации
ИЛИ Работники.ПодразделениеОрганизации.Родитель.Родитель = ДанныеСпецодежды.ПодразделениеОрганизации
Такой подход имеет серьезные архитектурные недостатки:
Родитель в языке запросов преобразуется СУБД в отдельную операцию LEFT JOIN к таблице справочника, что резко снижает производительность при работе с большими объемами данных.ПО) нельзя написать условие вида Таблица1.Подразделение В ИЕРАРХИИ (Таблица2.Подразделение). Платформа допускает справа от оператора В ИЕРАРХИИ только фиксированный параметр, список значений или временную таблицу, но не поле другой связываемой таблицы.Если задача решается программно в цикле без необходимости сложного соединения таблиц в запросе, оптимальным решением является штатный метод объектной модели ПринадлежитЭлементу(). Этот метод встроен в тип СправочникСсылка и выполняет рекурсивную проверку восходящей ветки.
Посмотрим на пример его использования:
Если ПодразделениеСотрудника.ПринадлежитЭлементу(ПодразделениеУсловия) Тогда
// ПодразделениеСотрудника входит в иерархическую ветку ПодразделениеУсловия
ВыполнитьДействие();
КонецЕсли;
Обратите внимание на важную особенность: метод возвращает Истина, только если проверяемый элемент является прямым или косвенным потомком переданного узла. Если проверяемый элемент совпадает с самим родителем, метод возвратит Ложь. Поэтому при необходимости полного включения условие дополняют проверкой на равенство ссылок:
ЭлементВходит = (ПодразделениеСотрудника = ПодразделениеУсловия)
ИЛИ ПодразделениеСотрудника.ПринадлежитЭлементу(ПодразделениеУсловия);
Когда требуется поднять список всех родителей элемента или найти самый верхний (корневой) узел, используем цикл с проверкой заполненности ссылки на родителя. Рассмотрим реализацию такого обхода:
СписокПредков = Новый Массив;
ТекущийУзел = ПодразделениеСотрудника;
Пока ЗначениеЗаполнено(ТекущийУзел.Родитель) Цикл
ТекущийУзел = ТекущийУзел.Родитель;
СписокПредков.Добавить(ТекущийУзел);
КонецЦикла;
Данный подход не зависит от глубины вложенности элементов в структуре предприятия и гарантированно доходит до корневого каталога.
В некоторых типовых и нетиповых конфигурациях применяется строгая иерархическая кодировка подразделений, где код подчиненного узла формируется путем конкатенации кода родителя и собственного суффикса (например: 2207 -> 220702 -> 22070201).
Если в системе соблюдается жесткое правило наследования префиксов кода, проверку вхождения можно выполнить через строковые функции:
ДлинаРодителя = СтрДлина(СокрЛП(КодРодителя));
Если Лев(СокрЛП(КодПотомка), ДлинаРодителя) = СокрЛП(КодРодителя) Тогда
// Код потомка начинается с кода родителя
КонецЕсли;
В тексте запроса аналогичная проверка реализуется с помощью строковых функций ПОДСТРОКА или оператора ПОДОБНО. Однако следует учитывать, что такой подход жестко завязан на формат кодирования и перестанет работать, если пользователь изменит код вручную без сохранения маски иерархии.
Для высоконагруженных систем, где необходимо часто соединять две большие таблицы в запросе по иерархическому признаку, наилучшим архитектурным решением является предварительный расчет иерархических связей.
Разберем по шагам, как устроен этот механизм:
СправочникСсылка.ПодразделенияОрганизаций: Элемент и Предок.ПриЗаписи справочника подразделений формируются записи регистра: для каждого подразделения записываются строки со всеми вышестоящими узлами, а также строка, где элемент ссылается сам на себя.
ВЫБРАТЬ
Работники.Сотрудник,
ДанныеСпецодежды.НормаВыдачи
ИЗ
ТаблицаРаботники КАК Работники
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИерархияПодразделений КАК Иерархия
ПО Работники.ПодразделениеОрганизации = Иерархия.Элемент
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаДанныеСпецодежды КАК ДанныеСпецодежды
ПО Иерархия.Предок = ДанныеСпецодежды.ПодразделениеОрганизации
Такой подход обеспечивает максимальную скорость выполнения запроса за счет использования стандартных индексов СУБД.
Если отчет или обработка данных строятся с помощью Системы компоновки данных, писать сложные соединения вручную не требуется. В СКД заложен штатный рекурсивный механизм связывания.
Для настройки восходящей иерархии выполним следующие действия:
Родитель с полем Ссылка.Выбор оптимального способа зависит от контекста задачи: для единичных проверок во встроенном языке используем ПринадлежитЭлементу(), для отчетов — функционал СКД, а для массивных связей в сложных запросах — вспомогательные таблицы иерархии.