Как правильно динамически формировать условия в запросе 1С и избежать ошибки «Не задано значение параметра»?

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

При разработке отчетов и обработок часто возникает необходимость накладывать условия отбора в запросе только в том случае, если пользователь заполнил соответствующие реквизиты на форме. Если подойти к формированию текста запроса некорректно, платформа выдает ошибку вида «Не задано значение параметра "Условие"» или синтаксические ошибки языка запросов. Рассмотрим подробнее, почему возникают такие ситуации и как правильно реализовывать динамические отборы.

Анализ типовых ошибок при динамической сборке запроса

Разберем исходную ситуацию, в которой разработчик попытался подставить условие в текст запроса через строковую замену:


ТекстЗапроса =  
"ВЫБРАТЬ
|    ЗначенияСвойствОбъектов.Объект КАК Объект,
|    ЗначенияСвойствОбъектов.Свойство КАК Свойство,
|    ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
|    РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ      
|    ТИПЗНАЧЕНИЯ(ЗначенияСвойствОбъектов.Объект)=Тип(Справочник.Контрагенты)
|   И &Условие";

Текст = СтрЗаменить(Текст,"&Условие",?(ЗначениеЗаполнено(Свойство),"ЗначенияСвойствОбъектов.Свойство в СПИСКЕ (&СписокСвойств)","ИСТИНА"));
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("СписокСвойств", Свойство);

Проанализируем ошибки, допущенные в этом фрагменте:

  1. Несоответствие имен переменных: исходный текст помещен в переменную ТекстЗапроса, а функция СтрЗаменить вызывается для необъявленной переменной Текст. В результате в Запрос.Текст передается исходная строка, в которой осталась конструкция &Условие. Платформа воспринимает символ & как объявление параметра запроса и требует установить его значение через метод УстановитьПараметр.
  2. Неверный синтаксис оператора вхождения: в языке запросов 1С не существует конструкции В СПИСКЕ. Для проверки вхождения в коллекцию используется ключевое слово В.
  3. Неоптимальная проверка типа: использование функции ТИПЗНАЧЕНИЯ(...) = ТИП(...) снижает производительность СУБД. Общепринятым платформенным стандартом является оператор ССЫЛКА.
  4. Ошибочное сопоставление полей: при отборе по значению свойства фильтр должен накладываться на поле Значение, а не на Свойство.

Способ 1. Динамическая подстановка условий через шаблоны строк

Рассмотрим, как исправить исходный алгоритм с использованием строковой замены заполнителей. Если условие не заполнено, маркер заменяется на логическую константу ИСТИНА, что позволяет не нарушать синтаксис секции ГДЕ.


ТекстЗапроса = 
"ВЫБРАТЬ
|	ЗначенияСвойствОбъектов.Объект КАК Объект,
|	ЗначенияСвойствОбъектов.Свойство КАК Свойство,
|	ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|	ЗначенияСвойствОбъектов.Объект ССЫЛКА Справочник.Контрагенты
|	И &УсловиеСвойство
|	И &УсловиеЗначение";

Если ЗначениеЗаполнено(Свойство) Тогда
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеСвойство", "ЗначенияСвойствОбъектов.Свойство В (&СписокСвойств)");
Иначе
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеСвойство", "ИСТИНА");
КонецЕсли;

Если ЗначениеЗаполнено(Значение) Тогда
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеЗначение", "ЗначенияСвойствОбъектов.Значение В (&СписокЗначений)");
Иначе
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеЗначение", "ИСТИНА");
КонецЕсли;

Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("СписокСвойств", Свойство);
Запрос.УстановитьПараметр("СписокЗначений", Значение);

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

Обратите внимание: если параметр передан в метод УстановитьПараметр, но не используется в итоговом тексте запроса (например, когда условие заменилось на ИСТИНА), платформа 1С не выдает ошибку, а просто игнорирует лишний параметр.

Способ 2. Сборка секции ГДЕ через массив условий (Рекомендуемый)

Более надежный и читаемый способ — динамическое формирование предложений секции ГДЕ при помощи массива и функции СтрСоединить. Этот подход исключает наличие лишних маркеров и конструкций И ИСТИНА в тексте запроса.

Разберем по шагам реализацию этого метода:


МассивУсловий = Новый Массив;

// Обязательное условие по типу ссылки
МассивУсловий.Добавить("ЗначенияСвойствОбъектов.Объект ССЫЛКА Справочник.Контрагенты");

// Опциональные отборы
Если ЗначениеЗаполнено(Свойство) Тогда
	МассивУсловий.Добавить("ЗначенияСвойствОбъектов.Свойство В (&СписокСвойств)");
КонецЕсли;

Если ЗначениеЗаполнено(Значение) Тогда
	МассивУсловий.Добавить("ЗначенияСвойствОбъектов.Значение В (&СписокЗначений)");
КонецЕсли;

ТекстУсловий = СтрСоединить(МассивУсловий, Символы.ПС + "	И ");

ТекстЗапроса = 
"ВЫБРАТЬ
|	ЗначенияСвойствОбъектов.Объект КАК Объект,
|	ЗначенияСвойствОбъектов.Свойство КАК Свойство,
|	ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|	" + ТекстУсловий;

Запрос = Новый Запрос(ТекстЗапроса);

Если ЗначениеЗаполнено(Свойство) Тогда
	Запрос.УстановитьПараметр("СписокСвойств", Свойство);
КонецЕсли;

Если ЗначениеЗаполнено(Значение) Тогда
	Запрос.УстановитьПараметр("СписокЗначений", Значение);
КонецЕсли;

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

Способ 3. Использование объекта СхемаЗапроса

Для сложных динамических выборок платформа предоставляет объект СхемаЗапроса. Он позволяет управлять текстом запроса через объектную модель, исключая ошибки строковой конкатенации.

Посмотрим на пример работы со схемой:


Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(
"ВЫБРАТЬ
|	ЗначенияСвойствОбъектов.Объект КАК Объект,
|	ЗначенияСвойствОбъектов.Свойство КАК Свойство,
|	ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|	ЗначенияСвойствОбъектов.Объект ССЫЛКА Справочник.Контрагенты");

ОператорВыбрать = Схема.ПакетЗапросов[0].Операторы[0];

Если ЗначениеЗаполнено(Свойство) Тогда
	ОператорВыбрать.Отбор.Добавить("ЗначенияСвойствОбъектов.Свойство В (&СписокСвойств)");
КонецЕсли;

Если ЗначениеЗаполнено(Значение) Тогда
	ОператорВыбрать.Отбор.Добавить("ЗначенияСвойствОбъектов.Значение В (&СписокЗначений)");
КонецЕсли;

Запрос = Новый Запрос(Схема.ПолучитьТекстЗапроса());
Запрос.УстановитьПараметр("СписокСвойств", Свойство);
Запрос.УстановитьПараметр("СписокЗначений", Значение);

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

Особенности работы оператора В

При передаче значений в оператор В важно помнить следующие правила:

  1. Оператор В одинаково корректно работает как с одиночным значением (ссылка, строка, число), так и с коллекциями (Массив, СписокЗначений).
  2. Если в параметр передать пустую коллекцию (например, пустой массив), условие Поле В (&Параметр) вернет ЛОЖЬ, и запрос не вернет ни одной строки. Поэтому проверять заполненность реквизита перед включением условия в запрос обязательно.
← На главную