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