Почему оператор "В ИЕРАРХИИ" в 1С не принимает сложные выражения для предопределенных значений?

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

При работе с запросами в 1С, особенно когда необходимо использовать иерархический поиск на основе значений, хранящихся в предопределенных элементах, программисты часто сталкиваются с неочевидными ограничениями. В частности, речь идет о попытке передать в оператор В ИЕРАРХИИ результат сложного выражения, такого как ВЫРАЗИТЬ(ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) КАК Справочник._СпециальныеНастройки).Значение. Рассмотрим эту ситуацию подробнее, выясним причину возникновения ошибки и предложим эффективные методы решения.

Суть проблемы: Недопустимое использование выражения в "В ИЕРАРХИИ"

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


ВЫБОР КОГДА Документы.Склад В ИЕРАРХИИ(ВЫРАЗИТЬ(ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) КАК Справочник._СпециальныеНастройки).Значение) ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ

Однако при попытке выполнить такой запрос, система 1С возвращает ошибку: "Неверные параметры 'В ИЕРАРХИИ'". Это может показаться странным, ведь само по себе выражение ВЫРАЗИТЬ(ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) КАК Справочник._СпециальныеНастройки).Значение прекрасно работает и возвращает нужную ссылку на группу складов, если использовать его, например, в списке полей или для простого сравнения.

Почему 1С не "нравится" такая конструкция?

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

Давайте разберем компоненты проблемного выражения:

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

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

Пример, демонстрирующий разницу между предопределенным элементом и его значением:


ВЫБРАТЬ
    ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) КАК СамПредопределенныйЭлемент,
    ВЫРАЗИТЬ(ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) КАК Справочник._СпециальныеНастройки).Значение КАК СсылкаИзРеквизита

Если мы выполним этот запрос, мы увидим две разные ссылки: первая будет указывать на элемент справочника _СпециальныеНастройки, а вторая — на объект, который хранится в его реквизите Значение (например, на группу справочника "Склады"). Оператор В ИЕРАРХИИ для Документы.Склад должен работать именно со СсылкаИзРеквизита, а не с СамПредопределенныйЭлемент, поскольку у СамПредопределенныйЭлемент иерархия относится к справочнику _СпециальныеНастройки, а нам нужна иерархия из справочника Склады.

Эффективные решения проблемы

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

1. Использование параметров запроса (рекомендуемый подход)

Этот способ является наиболее чистым и производительным, особенно если значение нужно получить один раз и затем использовать в запросе. Суть метода заключается в том, что мы сначала извлекаем нужную ссылку на группу (или элемент) с помощью встроенного языка 1С, а затем передаем ее в запрос как параметр.

Шаг 1: Получаем нужную ссылку на встроенном языке

Прежде чем выполнить запрос, мы должны получить значение реквизита Значение из предопределенного элемента _СпециальныеНастройки.ЦентральныеСклады. Делаем это с помощью стандартных средств встроенного языка 1С:


// Получаем предопределенный элемент
ПредопределенныйЭлемент = Справочники._СпециальныеНастройки.ЦентральныеСклады;

// Получаем значение его реквизита "Значение"
// Предполагаем, что реквизит "Значение" имеет составной тип
// и мы хотим получить из него конкретную ссылку на склад/группу складов
СсылкаНаЦентральныеСклады = ПредопределенныйЭлемент.Значение;

// Если СсылкаНаЦентральныеСклады может быть неопределенного типа,
// и мы точно знаем, что ожидаем Справочник.Склады.
// Можно добавить проверку или явное приведение, если необходимо.
// Например:
// Если ТипЗнч(СсылкаНаЦентральныеСклады) = Тип("СправочникСсылка.Склады") Тогда
//    // Используем ее
// Иначе
//    // Обработка ошибки или другая логика
// КонецЕсли;
Шаг 2: Передаем ссылку в запрос как параметр

Теперь, когда у нас есть готовая ссылка СсылкаНаЦентральныеСклады, мы можем передать ее в запрос как параметр. В самом запросе мы используем этот параметр напрямую в операторе В ИЕРАРХИИ.


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

Запрос.УстановитьПараметр("СсылкаНаЦентральныеСклады", СсылкаНаЦентральныеСклады);
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата()));

Результат = Запрос.Выполнить().Выгрузить();

Этот подход гарантирует, что оператор В ИЕРАРХИИ получает на вход уже вычисленную и корректную ссылку, что полностью соответствует его синтаксическим требованиям и логике работы.

2. Использование временных таблиц

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

Шаг 1: Вычисляем и помещаем ссылку во временную таблицу

Создаем первый блок запроса, который извлекает нужную ссылку и помещает ее во временную таблицу втСсылкаНаЦентральныеСклады.


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
    ВЫРАЗИТЬ(ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) КАК Справочник._СпециальныеНастройки).Значение КАК СсылкаНаЦентральныеСклады
ПОМЕСТИТЬ втСсылкаНаЦентральныеСклады
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Документы.Ссылка,
    Документы.Склад,
    ВЫБОР
        КОГДА Документы.Склад В ИЕРАРХИИ(втСсылкаНаЦентральныеСклады.СсылкаНаЦентральныеСклады)
        ТОГДА ИСТИНА
        ИНАЧЕ ЛОЖЬ
    КОНЕЦ КАК ЭтоЦентральныйСклад
ИЗ
    Документ.РеализацияТоваровУслуг КАК Документы,
    втСсылкаНаЦентральныеСклады КАК втСсылкаНаЦентральныеСклады
ГДЕ
    Документы.Дата МЕЖДУ &НачалоПериода И &КонецПериода";

Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата()));

Результат = Запрос.Выполнить().Выгрузить();

В этом примере мы сначала получаем значение реквизита Значение предопределенного элемента и помещаем его в колонку СсылкаНаЦентральныеСклады временной таблицы. Затем в основном запросе мы можем присоединиться к этой временной таблице и использовать ее колонку как параметр для В ИЕРАРХИИ.

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
    Документы.Ссылка,
    Документы.Склад,
    ВЫБОР
        КОГДА Документы.Склад В ИЕРАРХИИ(
            ВЫБРАТЬ
                ВЫРАЗИТЬ(ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) КАК Справочник._СпециальныеНастройки).Значение
            ИЗ
                Справочник._СпециальныеНастройки КАК Справочник_СпециальныеНастройки // Нужно чтобы был хоть какой-то источник для подзапроса
            ГДЕ
                Справочник_СпециальныеНастройки.Ссылка = ЗНАЧЕНИЕ(Справочник._СпециальныеНастройки.ЦентральныеСклады) // Уточняем, если нужно, хотя для ЗНАЧЕНИЕ() это избыточно
        )
        ТОГДА ИСТИНА
        ИНАЧЕ ЛОЖЬ
    КОНЕЦ КАК ЭтоЦентральныйСклад
ИЗ
    Документ.РеализацияТоваровУслуг КАК Документы
ГДЕ
    Документы.Дата МЕЖДУ &НачалоПериода И &КонецПериода";

Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата()));

Результат = Запрос.Выполнить().Выгрузить();

В этом варианте мы используем подзапрос, который возвращает единственное значение — ссылку на группу складов. Этот подзапрос является простым и не содержит сложных вычислений внутри самого оператора В ИЕРАРХИИ, что позволяет системе запросов 1С его корректно обработать.

Особенности использования `ВЫРАЗИТЬ` и `ЗНАЧЕНИЕ`

Давайте еще раз закрепим понимание функций ВЫРАЗИТЬ и ЗНАЧЕНИЕ в контексте запросов 1С.

Заключение

Оператор В ИЕРАРХИИ в запросах 1С является мощным инструментом для работы с иерархическими данными. Однако, как мы выяснили, он имеет определенные ограничения на тип передаваемых параметров. Он ожидает либо прямую ссылку, либо простой подзапрос, возвращающий ссылки, а не сложное выражение, требующее многоступенчатых вычислений. Это не является "багом", а скорее особенностью синтаксиса и логики выполнения запросов 1С.

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

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

Выбор конкретного метода зависит от ваших предпочтений, сложности запроса и требований к производительности. Главное — всегда помнить, что В ИЕРАРХИИ ждет "готовую" ссылку, а не инструкцию по ее вычислению из сложной структуры данных.

← На главную