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

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

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

Стандартный оператор языка запросов Элемент В ИЕРАРХИИ (&Группа) не предназначен для работы в формате &Параметр В ИЕРАРХИИ (Элемент). Давайте разберем, почему это так, и проанализируем основные способы решения этой технической проблемы.

Суть проблемы: поиск вниз vs поиск вверх

Стандартный поиск «вниз» эффективно оптимизирован платформой. При выполнении запроса 1С использует либо специальные индексы иерархии в СУБД, либо строит условия, охватывающие всех потомков. Когда же нам нужно проверить «вверх», мы сталкиваемся с тем, что у одного элемента может быть только один непосредственный родитель, но целая цепочка предков до самого корня. В языке запросов 1С отсутствуют рекурсивные запросы (аналоги WITH RECURSIVE в SQL), что вынуждает нас искать обходные пути.

Способ 1. Итеративный обход в коде (Запрос в цикле)

Рассмотрим самый простой, но часто критикуемый метод — получение всех родителей элемента во временный массив перед выполнением основного запроса. Несмотря на известную фразу о том, что «запросы в цикле — это плохо», для одиночного объекта этот метод может быть самым быстрым в реализации.

Проанализируем алгоритм:

  1. Берем исходный элемент справочника.
  2. В цикле получаем его родителя, пока родитель не станет пустым.
  3. Всех найденных родителей помещаем в массив.
  4. Передаем этот массив в запрос и используем обычный оператор В.

Пример реализации на языке 1С:


МассивПредков = Новый Массив;
ТекущийРодитель = ОбъектСсылка.Родитель;

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

// Теперь используем МассивПредков в запросе
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ &ИскомаяГруппа В (&МассивПредков)";

Преимущества: простота реализации, не требует изменения структуры метаданных.
Недостатки: низкая производительность при массовой обработке данных (например, в динамическом списке или сложном отчете).

Способ 2. Денормализация данных: Реквизит «Головной родитель»

Если ваша задача — часто проверять принадлежность элемента к определенной крупной ветке справочника (например, «Договоры аренды» или «Договоры купли-продажи»), рассмотрим метод денормализации. Выясним причину популярности этого метода в высоконагруженных системах: он позволяет перенести вычисления из момента выполнения запроса в момент записи объекта.

Разберем шаги реализации:

  1. Добавим в справочник Договоры новый реквизит ГоловнойРодитель (тип СправочникСсылка.Договоры).
  2. В модуле объекта в событии ПередЗаписью пропишем логику поиска самого верхнего узла иерархии.
  3. Проиндексируем данный реквизит.

Теперь наш запрос будет выглядеть максимально элегантно и работать мгновенно:


ВЫБРАТЬ
    Договоры.Ссылка
ИЗ
    Справочник.Договоры КАК Договоры
ГДЕ
    Договоры.ГоловнойРодитель = &ПараметрГруппаДоговоров

Как отмечалось в обсуждении, вместо реквизита можно использовать Регистр сведений, если вы не хотите менять структуру самого справочника или если у элемента может быть несколько «смысловых» корней.

Способ 3. Использование механизма «Полный код» (LIKE)

Посмотрим на ситуацию под другим углом. В некоторых конфигурациях используется иерархия групп и элементов, а коды справочников строятся иерархически. Если мы можем гарантировать, что код элемента содержит коды всех его родителей (так называемый Materialized Path), то задача решается текстовым сравнением.

В запросе это реализуется через оператор ПОДОБНО. Рассмотрим пример:


ВЫБРАТЬ
    Договоры.Ссылка
ИЗ
    Справочник.Договоры КАК Договоры
ГДЕ
    Договоры.Код ПОДОБНО &КодГруппы + "%"

Важно помнить, что этот метод специфичен и сильно зависит от настроек нумерации в конкретной базе данных.

Способ 4. Метод левых соединений (Эмуляция рекурсии в запросе)

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

Рассмотрим пример запроса для поиска предков до 3-го уровня:


ВЫБРАТЬ
    Договоры.Ссылка,
    ЕСТЬNULL(Договоры.Родитель, ЗНАЧЕНИЕ(Справочник.Договоры.ПустаяСсылка)) КАК Родитель1,
    ЕСТЬNULL(Договоры.Родитель.Родитель, ЗНАЧЕНИЕ(Справочник.Договоры.ПустаяСсылка)) КАК Родитель2,
    ЕСТЬNULL(Договоры.Родитель.Родитель.Родитель, ЗНАЧЕНИЕ(Справочник.Договоры.ПустаяСсылка)) КАК Родитель3
ПОМЕСТИТЬ ВТ_ВсеПредки
ИЗ
    Справочник.Договоры КАК Договоры
ГДЕ
    Договоры.Ссылка = &ЭлементДляПроверки;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ИСТИНА
ИЗ
    ВТ_ВсеПредки КАК Предки
ГДЕ
    &ИскомаяГруппа В (Предки.Родитель1, Предки.Родитель2, Предки.Родитель3)

Хотя этот метод кажется громоздким, он позволяет в рамках одного обращения к БД проверить принадлежность к иерархии на определенную глубину.

Способ 5. Возможности Системы компоновки данных (СКД)

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

Алгоритм настройки в СКД:

  1. Создаем набор данных — запрос.
  2. На закладке «Связи наборов данных» связываем набор сам с собой.
  3. Источник связи: Родитель, Приемник связи: Ссылка.
  4. Указываем параметр условия связи для управления иерархией.

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

Резюме и рекомендации

Выяснив все доступные варианты, мы можем подвести итог:

Помните, что архитектурно правильное решение всегда зависит от контекста. Если иерархия справочника небольшая (2–3 уровня), даже «наивный» обход в запросе через несколько точек (Родитель.Родитель) может быть оправдан своей простотой.

← На главную