Как получить значение реквизита из иерархии справочника (наследование от родителя) в запросе 1С

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

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

Метод 1: Использование временных таблиц и группировки по приоритету

Проанализируем ситуацию, когда мы заранее не знаем, на каком уровне иерархии заполнено значение, но хотим выбрать «ближайшее» к элементу. Для этого мы можем использовать механизм объединения (ОБЪЕДИНИТЬ ВСЕ) с назначением весовых коэффициентов (приоритетов) каждому уровню.

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

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

Посмотрим на пример реализации такого запроса:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    Номенклатура.МестоХранения КАК МестоХранения,
    1 КАК Приоритет
ПОМЕСТИТЬ ВТ_ВсеУровни
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В (&СписокНоменклатуры)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Родитель.МестоХранения,
    2
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В (&СписокНоменклатуры)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Родитель.Родитель.МестоХранения,
    3
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В (&СписокНоменклатуры)
;

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

Важный нюанс: при группировке по МИНИМУМ(Приоритет) обязательно нужно отсекать пустые значения (NULL или ПустаяСсылка) еще на этапе формирования выборки для группировки. В противном случае запрос найдет минимальный приоритет (например, 1 — сам элемент), но значение там будет пустым, и вы не перейдете к родителю.

Метод 2: Каскадное использование ЕСТЬNULL (Coalesce)

Выясним причину, почему этот метод часто считается более предпочтительным. Использование ЛЕВОЕ СОЕДИНЕНИЕ позволяет получить результат в один проход без использования тяжелых объединений UNION, которые СУБД иногда сложно оптимизировать.

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


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

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

Чтобы обойти это ограничение, в запросе следует использовать конструкцию ВЫБОР:


ВЫБРАТЬ
    Ном.Ссылка КАК Номенклатура,
    ВЫБОР
        КОГДА Ном.МестоХранения <> ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка) ТОГДА Ном.МестоХранения
        КОГДА Род1.МестоХранения <> ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка) ТОГДА Род1.МестоХранения
        КОГДА Род2.МестоХранения <> ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка) ТОГДА Род2.МестоХранения
        ИНАЧЕ ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка)
    КОНЕЦ КАК МестоХраненияИтого
...

Особенности работы с иерархией и производительность

При работе с большими справочниками (более 100 000 элементов) необходимо учитывать следующие рекомендации:

Сравнение методов

Рассмотрим, какой метод выбрать в зависимости от условий задачи:

Метод с приоритетами (UNION):

Метод с соединениями (JOIN):

В завершение отметим, что если задача «наследования» свойств стоит очень остро и уровней иерархии много, стоит рассмотреть архитектурное решение: использование Регистров сведений или механизма Дополнительных реквизитов (БСП), в которых логика поиска по иерархии часто уже реализована на программном уровне или оптимизирована платформой.

← На главную