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

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

При разработке запросов в платформе 1С:Предприятие часто возникает задача отбора строк, где реквизитом может выступать как конечный элемент, так и группа справочника. Типичная ситуация: в строках табличной части указаны папки или элементы справочника (например, группы контрагентов), а в качестве параметра запроса &НашКонтрагент передается конкретный подчиненный элемент.

Попытка написать условие вида (&НашКонтрагент) В ИЕРАРХИИ (Таблица.Контрагент) приводит к синтаксической ошибке. Выясним причину такого поведения платформы и разберем несколько проверенных вариантов реализации этой логики.

Почему не работает стандартная конструкция В ИЕРАРХИИ

Оператор В ИЕРАРХИИ в языке запросов 1С работает строго «сверху вниз». Его синтаксис жестко регламентирован:

  1. Слева от оператора всегда должно находиться проверяемое поле таблицы (потомок).
  2. Справа от оператора в скобках должен передаваться список предопределенных узлов (родителей) — параметр, массив параметров или подзапрос.

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

Способ 1. Предварительный сбор цепочки родителей в коде 1С (рекомендуемый)

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

Посмотрим на пример кода сбора всех вышестоящих групп:


МассивРодителей = Новый Массив;
МассивРодителей.Добавить(НашКонтрагент);

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

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Задачи.Ссылка КАК Задача,
    |    Задачи.Контрагент КАК Контрагент
    |ИЗ
    |    Документ.ЗадачиПользователя.Контрагенты КАК Задачи
    |ГДЕ
    |    Задачи.Контрагент В (&РодителиЭлемента)";

Запрос.УстановитьПараметр("РодителиЭлемента", МассивРодителей);
РезультатЗапроса = Запрос.Выполнить();

Преимущества данного подхода:

  1. Условие В (&РодителиЭлемента) выполняется на уровне СУБД максимально быстро по простому индексу.
  2. Не создаются лишние тяжелые соединения таблиц.
  3. Работает при любой глубине иерархии без ограничений.

Способ 2. Проверка уровней вложенности через цепочку родителей в запросе

Если справочник имеет фиксированное небольшое число уровней вложенности (в свойствах метаданных включено Ограничивать количество уровней), можно развернуть проверку через условие ИЛИ.

Рассмотрим пример условия в секции ГДЕ:


ГДЕ
    ЗадачиПользователяКонтрагенты.Контрагент = ВЫРАЗИТЬ(&НашКонтрагент КАК Справочник.Контрагенты)
    ИЛИ ЗадачиПользователяКонтрагенты.Контрагент = ВЫРАЗИТЬ(&НашКонтрагент КАК Справочник.Контрагенты).Родитель
    ИЛИ ЗадачиПользователяКонтрагенты.Контрагент = ВЫРАЗИТЬ(&НашКонтрагент КАК Справочник.Контрагенты).Родитель.Родитель

Особенности и ограничения:

  1. Каждое обращение через точку к полю Родитель порождает неявное левое соединение (LEFT JOIN) в итоговом SQL-запросе.
  2. Если глубина иерархии в справочнике не ограничена или превысит количество условий в запросе, часть вышестоящих групп не попадет в выборку.

Способ 3. Построение дерева родителей через транзитивное замыкание

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

Разберем логику построения:

  1. Во временную таблицу помещаются прямые пары Ссылка - Родитель.
  2. С помощью серии соединений таблицы самой с собой формируется полная матрица связей Элемент - ВсеРодители.
  3. Основная таблица связывается с полученной иерархической таблицей.

Посмотрим на структуру связывания в запросе:


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

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

← На главную