Как проверить принадлежность элемента родителю и выбрать данные вверх по иерархии в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке запросов и алгоритмов обработки данных в 1С часто возникает задача определить, входит ли подчиненный элемент в определенную группу или вышестоящее подразделение. Стандартная конструкция запроса В ИЕРАРХИИ отлично работает в направлении «сверху вниз», когда необходимо отобрать все дочерние узлы для заданной группы. Однако когда требуется проверить связь «снизу вверх» (от подчиненного узла к родителям) или связать две таблицы по признаку вхождения, разработчики нередко сталкиваются с трудностями и громоздкими конструкциями через обращение к свойству Родитель через точку.

Разберем детально, почему прямое обращение через цепочку родителей не всегда эффективно, и рассмотрим правильные варианты реализации этой задачи как на встроенном языке, так и в механизмах запросов и СКД.

Ограничения стандартных конструкций и обращений через точку

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


Работники.ПодразделениеОрганизации = ДанныеСпецодежды.ПодразделениеОрганизации
ИЛИ Работники.ПодразделениеОрганизации.Родитель = ДанныеСпецодежды.ПодразделениеОрганизации
ИЛИ Работники.ПодразделениеОрганизации.Родитель.Родитель = ДанныеСпецодежды.ПодразделениеОрганизации

Такой подход имеет серьезные архитектурные недостатки:

  1. Неопределенная глубина иерархии: если в свойствах справочника не задано жесткое ограничение количества уровней, добавление нового промежуточного уровня в дереве подразделений приведет к тому, что условие перестанет находить вышестоящего предка.
  2. Избыточные левые соединения в SQL: каждое обращение через точку к реквизиту Родитель в языке запросов преобразуется СУБД в отдельную операцию LEFT JOIN к таблице справочника, что резко снижает производительность при работе с большими объемами данных.
  3. Ограничение оператора связи: в условиях соединения двух таблиц (секция ПО) нельзя написать условие вида Таблица1.Подразделение В ИЕРАРХИИ (Таблица2.Подразделение). Платформа допускает справа от оператора В ИЕРАРХИИ только фиксированный параметр, список значений или временную таблицу, но не поле другой связываемой таблицы.

Вариант 1. Использование метода ПринадлежитЭлементу() во встроенном языке

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

Посмотрим на пример его использования:


Если ПодразделениеСотрудника.ПринадлежитЭлементу(ПодразделениеУсловия) Тогда
    // ПодразделениеСотрудника входит в иерархическую ветку ПодразделениеУсловия
    ВыполнитьДействие();
КонецЕсли;

Обратите внимание на важную особенность: метод возвращает Истина, только если проверяемый элемент является прямым или косвенным потомком переданного узла. Если проверяемый элемент совпадает с самим родителем, метод возвратит Ложь. Поэтому при необходимости полного включения условие дополняют проверкой на равенство ссылок:


ЭлементВходит = (ПодразделениеСотрудника = ПодразделениеУсловия) 
    ИЛИ ПодразделениеСотрудника.ПринадлежитЭлементу(ПодразделениеУсловия);

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

Когда требуется поднять список всех родителей элемента или найти самый верхний (корневой) узел, используем цикл с проверкой заполненности ссылки на родителя. Рассмотрим реализацию такого обхода:


СписокПредков = Новый Массив;
ТекущийУзел = ПодразделениеСотрудника;

Пока ЗначениеЗаполнено(ТекущийУзел.Родитель) Цикл
    ТекущийУзел = ТекущийУзел.Родитель;
    СписокПредков.Добавить(ТекущийУзел);
КонецЦикла;

Данный подход не зависит от глубины вложенности элементов в структуре предприятия и гарантированно доходит до корневого каталога.

Вариант 3. Анализ префиксов и подчиненности по коду элемента

В некоторых типовых и нетиповых конфигурациях применяется строгая иерархическая кодировка подразделений, где код подчиненного узла формируется путем конкатенации кода родителя и собственного суффикса (например: 2207 -> 220702 -> 22070201).

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


ДлинаРодителя = СтрДлина(СокрЛП(КодРодителя));
Если Лев(СокрЛП(КодПотомка), ДлинаРодителя) = СокрЛП(КодРодителя) Тогда
    // Код потомка начинается с кода родителя
КонецЕсли;

В тексте запроса аналогичная проверка реализуется с помощью строковых функций ПОДСТРОКА или оператора ПОДОБНО. Однако следует учитывать, что такой подход жестко завязан на формат кодирования и перестанет работать, если пользователь изменит код вручную без сохранения маски иерархии.

Вариант 4. Денормализация иерархии через регистр сведений (Closure Table)

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

Разберем по шагам, как устроен этот механизм:

  1. Создается вспомогательный непериодический регистр сведений, содержащий два ключевых измерения типа СправочникСсылка.ПодразделенияОрганизаций: Элемент и Предок.
  2. В подписке на событие ПриЗаписи справочника подразделений формируются записи регистра: для каждого подразделения записываются строки со всеми вышестоящими узлами, а также строка, где элемент ссылается сам на себя.
  3. В результирующем запросе соединение выполняется по простым равенствам без использования тяжелых иерархических условий:

ВЫБРАТЬ
    Работники.Сотрудник,
    ДанныеСпецодежды.НормаВыдачи
ИЗ
    ТаблицаРаботники КАК Работники
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИерархияПодразделений КАК Иерархия
        ПО Работники.ПодразделениеОрганизации = Иерархия.Элемент
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаДанныеСпецодежды КАК ДанныеСпецодежды
        ПО Иерархия.Предок = ДанныеСпецодежды.ПодразделениеОрганизации

Такой подход обеспечивает максимальную скорость выполнения запроса за счет использования стандартных индексов СУБД.

Вариант 5. Использование рекурсивных связей в Системе компоновки данных (СКД)

Если отчет или обработка данных строятся с помощью Системы компоновки данных, писать сложные соединения вручную не требуется. В СКД заложен штатный рекурсивный механизм связывания.

Для настройки восходящей иерархии выполним следующие действия:

  1. Перейдем на вкладку Связи наборов данных.
  2. Укажем набор данных в качестве источника и его же в качестве приемника.
  3. В поле «Условие связи» свяжем поле Родитель с полем Ссылка.
  4. Заполним параметры начального значения связи. При такой настройке СКД автоматически выполнит рекурсивную выборку всех вышестоящих родителей до самого верха дерева.

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

← На главную