Как в запросе 1С проверить, является ли значение поля родителем для конкретного элемента (обратная иерархия)

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

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

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

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

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

Нам необходимо получить все скидки, действующие для конкретного Склада 102. Логика должна быть следующей:

  1. Выбрать скидки, где в поле Склад указан непосредственно сам Склад 102.
  2. Выбрать скидки, где в поле Склад указана группа-родитель (например, Оптовые склады район 1).
  3. Выбрать скидки, где указана вышестоящая группа (например, Оптовые склады).

Проблема в том, что обычное условие ГДЕ &Склад В ИЕРАРХИИ(Склад) не работает. Нам нужно «развернуть» логику и найти всех предков нашего склада.

Решение №1: Использование временной таблицы для поиска всех родителей

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

Рассмотрим по шагам, как это реализовать в коде запроса:


// ШАГ 1: Получаем все родительские группы и сам элемент во временную таблицу
ВЫБРАТЬ
    Склады.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ВсеРодители
ИЗ
    Справочник.Склады КАК Склады
ГДЕ
    &СкладПараметр В ИЕРАРХИИ(Склады.Ссылка);

// ШАГ 2: Основной запрос к регистру сведений
ВЫБРАТЬ
    Скидки.СкидкаНаценка,
    Скидки.Склад
ИЗ
    РегистрСведений.ДействиеСкидокНаценокНаСкладах.СрезПоследних(&Дата, ) КАК Скидки
ГДЕ
    Скидки.Склад В (ВЫБРАТЬ Ссылка ИЗ ВТ_ВсеРодители)
    И Скидки.Статус = &Статус;

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

Решение №2: Использование подзапроса в условии

Если по каким-то причинам мы не хотим использовать менеджер временных таблиц (например, в простом динамическом списке), мы можем перенести логику в подзапрос прямо внутри условия ГДЕ. Это решение выглядит компактнее, но функционально идентично первому.

Разберем пример такого запроса:


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

Проанализируем этот подход: СУБД сначала выполнит вложенный запрос, сформирует список ссылок-родителей, а затем сопоставит их с полем Склад в регистре. Это эффективное решение для иерархических справочников любой вложенности.

Решение №3: Левое соединение со справочником (Join)

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


ВЫБРАТЬ
    Скидки.СкидкаНаценка,
    Скидки.Склад КАК СкладИзРегистра
ИЗ
    РегистрСведений.ДействиеСкидокНаценокНаСкладах.СрезПоследних(&Дата, ) КАК Скидки
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Склады КАК СпрСклады
    ПО Скидки.Склад = СпрСклады.Ссылка
ГДЕ
    &СкладПараметр В ИЕРАРХИИ(СпрСклады.Ссылка);

В данном примере мы соединяем таблицу скидок со справочником складов. Условие В ИЕРАРХИИ в секции ГДЕ фильтрует уже соединенную таблицу. Важно понимать, что этот метод может быть чуть менее производительным на огромных таблицах, чем использование временной таблицы, так как соединение происходит до фильтрации по иерархии (хотя оптимизатор SQL-запросов часто нивелирует эту разницу).

Важные нюансы и оптимизация

Рассмотрим несколько моментов, которые помогут избежать ошибок при реализации:

  1. Тип иерархии: Метод с поиском всех родителей универсален. Он работает как для иерархии «Групп и элементов», так и для иерархии «Элементов». Если ваш справочник настроен так, что родителем может быть другой элемент, код останется прежним.
  2. Тип данных: Убедитесь, что поле в регистре имеет тип СправочникСсылка.Склады. Если тип составной, использование индексов при проверке иерархии может быть затруднено, что замедлит запрос. В таких случаях полезно использовать функцию ВЫРАЗИТЬ(Скидки.Склад КАК Справочник.Склады).
  3. Пустые значения: Если параметр &СкладПараметр будет пустым, запрос может вернуть непредсказуемый результат (или вообще ничего не вернуть в зависимости от настроек). Рекомендуем добавить проверку на пустую ссылку перед выполнением запроса.
  4. Многоуровневость: Предложенные решения корректно обрабатывают любую глубину вложенности. Независимо от того, является ли склад «внуком» или «правнуком» группы, он будет найден, так как оператор В ИЕРАРХИИ транзитивен по своей природе.

Итоги

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

Наиболее предпочтительным методом является использование временной таблицы (Решение №1), так как оно разделяет логику поиска структуры и логику выбора данных из целевого регистра, что упрощает отладку и поддержку кода. Теперь вы можете гибко настраивать скидки, права доступа или любые другие параметры, которые должны наследоваться «сверху вниз» от групп к конкретным элементам в 1С.

← На главную