При разработке на платформе «1С:Предприятие» конструкция В ИЕРАРХИИ является одним из наиболее востребованных инструментов для работы со справочниками, имеющими иерархическую структуру (например, подразделения, номенклатура, контрагенты или склады). Однако при ее использовании программисты нередко сталкиваются с неприятной ошибкой: «Неверные параметры». Эта ошибка может возникать на этапе компиляции запроса или непосредственно во время его выполнения.
В этой статье мы подробно разберем, почему возникает данная ошибка, проанализируем реальный кейс с форума разработчиков, а также рассмотрим другие неочевидные нюансы и ограничения работы с оператором В ИЕРАРХИИ, которые могут привести к падению производительности или сбоям в системе.
Рассмотрим классическую ситуацию, с которой столкнулся автор темы. В коде выполнялся стандартный запрос по выборке подразделений с отбором по иерархии:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Подразделения.Ссылка КАК Ссылка,
Подразделения.Родитель КАК Родитель
ИЗ
Справочник.Подразделения КАК Подразделения
ГДЕ
Подразделения.Ссылка В ИЕРАРХИИ(&спДоступныеПодразделения)
ИТОГИ ПО
Ссылка,
Родитель
ТОЛЬКО ИЕРАРХИЯ
При выполнении этого кода платформа возвращала ошибку неверных параметров. При этом визуальная проверка параметра спДоступныеПодразделения показывала, что список заполнен элементами нужного типа (справочник Подразделения).
В чем крылась ошибка? Проблема заключалась в способе заполнения списка значений. При программном обходе одного списка значений и переносе его элементов в другой список была допущена популярная опечатка: вместо ссылки на элемент справочника (свойства Значение) в новый список добавлялся сам объект ЭлементСпискаЗначений.
Рассмотрим подробнее схему возникновения этой ошибки на примере кода:
// ОШИБОЧНЫЙ ВАРИАНТ ЗАПОЛНЕНИЯ
СписокДляЗапроса = Новый СписокЗначений;
Для Каждого ЭлементИзИсходногоСписка Из ИсходныйСписок Цикл
// Здесь совершается ошибка: в список добавляется сам объект "ЭлементСпискаЗначений"
СписокДляЗапроса.Добавить(ЭлементИзИсходногоСписка);
КонецЦикла;
Запрос.УстановитьПараметр("спДоступныеПодразделения", СписокДляЗапроса);
Для платформы 1С элементы списка значений являются системными объектами типа ЭлементСпискаЗначений, которые содержат свойства Значение, Представление, Пометка и Картинка. СУБД не умеет сопоставлять системный тип 1С с полями базы данных, что и приводит к ошибке несовместимости типов в конструкции В ИЕРАРХИИ.
Правильное решение проблемы: всегда извлекайте свойство Значение при переносе данных или заполнении параметров:
// ИСПРАВЛЕННЫЙ И БЕЗОПАСНЫЙ ВАРИАНТ
СписокДляЗапроса = Новый СписокЗначений;
Для Каждого ЭлементИзИсходногоСписка Из ИсходныйСписок Цикл
// Извлекаем конкретную ссылку на справочник через свойство ".Значение"
СписокДляЗапроса.Добавить(ЭлементИзИсходногоСписка.Значение);
КонецЦикла;
Запрос.УстановитьПараметр("спДоступныеПодразделения", СписокДляЗапроса);
Еще одно жесткое ограничение платформы 1С, о котором должен знать каждый разработчик: конструкцию В ИЕРАРХИИ категорически запрещено использовать в секциях связи таблиц (ПО / JOIN).
Попытка написать запрос следующего вида гарантированно вызовет ошибку «Неверные параметры»:
// ТАК ДЕЛАТЬ НЕЛЬЗЯ!
ВЫБРАТЬ
Товары.Ссылка,
Продажи.Количество
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи КАК Продажи
ПО Товары.Ссылка В ИЕРАРХИИ (Продажи.Номенклатура)
Конструкция В ИЕРАРХИИ может использоваться только в секции отбора ГДЕ (WHERE) или в параметрах отбора виртуальных таблиц.
Платформа 1С строго регламентирует порядок написания условий. Конструкция ожидает, что слева от оператора В ИЕРАРХИИ находится проверяемое поле базы данных (например, Номенклатура.Ссылка), а справа в скобках — конкретное значение-ориентир (ссылка, группа, массив или список значений).
Если написать условие наоборот, например: &ПараметрГруппа В ИЕРАРХИИ (Номенклатура.Ссылка), то запрос вернет ошибку неверных параметров.
Выделим еще несколько ситуаций, когда параметры запроса приводят к падению системы:
СправочникМенеджер.Подразделения вместо конкретной ссылки или списка. Это некорректно.Неопределено: Если переменная, передаваемая в параметр, не инициализирована или равна Неопределено, СУБД вернет ошибку типов при анализе иерархии. Перед установкой параметров всегда проверяйте тип данных.СправочникСсылка. Для корректной работы оператора В ИЕРАРХИИ в консоли необходимо явно указать конкретный тип данных (например, СправочникСсылка.Подразделения).Отдельно проанализируем ситуацию, когда запрос отрабатывает без ошибок, но намертво вешает систему. Это происходит, когда в параметр конструкции В ИЕРАРХИИ передается пустая ссылка (например, Справочник.Подразделения.ПустаяСсылка()).
Для СУБД пустая ссылка является корневым элементом. Пытаясь найти все элементы, входящие в иерархию "пустоты", СУБД начинает сканировать абсолютно весь справочник, выстраивая тяжелые рекурсивные связи. На больших базах данных это приводит к колоссальному падению производительности и блокировкам таблиц.
Рекомендация по стандартам разработки 1С: перед запуском запроса всегда проверяйте переданные параметры на заполненность. Если параметр пустой — условие иерархии нужно полностью исключать из текста запроса программно:
ТекстЗапроса =
"ВЫБРАТЬ
| Подразделения.Ссылка КАК Ссылка
|ИЗ
| Справочник.Подразделения КАК Подразделения";
// Проверяем, заполнена ли группа
Если ЗначениеЗаполнено(ГруппаОтбора) Тогда
ТекстЗапроса = ТекстЗапроса + " ГДЕ Подразделения.Ссылка В ИЕРАРХИИ(&ГруппаОтбора)";
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапроса);
Если ЗначениеЗаполнено(ГруппаОтбора) Тогда
Запрос.УстановитьПараметр("ГруппаОтбора", ГруппаОтбора);
КонецЕсли;
Использование оператора В ИЕРАРХИИ непосредственно внутри параметров виртуальных таблиц (например, в параметрах отбора регистра остатков или оборотов) крайне нежелательно с точки зрения производительности. СУБД тратит много ресурсов на одновременный расчет иерархии и сбор итогов по регистру.
Выясним, как оптимизировать такую выборку. Правильным архитектурным решением будет предварительное получение плоского списка всех подчиненных элементов во временную таблицу, с последующим использованием быстрого оператора соединения или оператора В.
Проанализируем пример оптимизированного запроса:
ВЫБРАТЬ
Склады.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_РазрешенныеСклады
ИЗ
Справочник.Склады КАК Склады
ГДЕ
Склады.Ссылка В ИЕРАРХИИ(&ГруппаСкладов)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ПродажиОбороты.Номенклатура,
ПродажиОбороты.КоличествоОборот
ИЗ
РегистрНакопления.Продажи.Обороты(, , , Склад В (ВЫБРАТЬ Ссылка ИЗ ВТ_РазрешенныеСклады)) КАК ПродажиОбороты
Этот подход позволяет СУБД сначала быстро построить простой и понятный плоский список складов во временной таблице ВТ_РазрешенныеСклады, а затем мгновенно наложить легкий фильтр на виртуальную таблицу оборотов без тяжелых рекурсивных вычислений «на лету».