При разработке отчетов и обработок на платформе 1С программисты часто сталкиваются с задачей реализации необязательного отбора. Например, если пользователь выбрал список групп номенклатуры, запрос должен отфильтровать данные по ним, а если список пуст — вывести всю информацию без ограничений.
Попытка проверить заполненность параметра непосредственно внутри текста запроса с помощью конструкции вида КОГДА &ГруппыТоваров <> НЕОПРЕДЕЛЕНО приводит к синтаксической ошибке:
«Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов.»
Проанализируем причину возникновения этой проблемы и разберем корректные способы реализации опциональных условий в языке запросов 1С.
Язык запросов 1С строг к типам данных, участвующим в операциях сравнения (=, <>). Когда в параметр &ГруппыТоваров передается массив или список значений, платформа воспринимает его как коллекцию. Язык запросов не умеет напрямую сравнивать коллекцию со скалярным значением НЕОПРЕДЕЛЕНО на уровне СУБД. Это приводит к ошибке еще на этапе проверки типов перед отправкой SQL-запроса.
Рассмотрим два основных подхода, позволяющих решить эту задачу правильно и без ошибок.
Самый простой способ обойти ограничение языка запросов — передать в запрос два параметра: сам массив значений и булев флаг, указывающий, нужно ли применять отбор.
Разберем реализацию этого способа на стороне программного кода 1С:
ЕстьОтбор = ГруппыНоменклатуры.Количество() > 0;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыОстатки.Номенклатура КАК Номенклатура,
| ТоварыОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыОстатки
|ГДЕ
| ВЫБОР
| КОГДА &ИспользоватьОтбор
| ТОГДА ТоварыОстатки.Номенклатура В ИЕРАРХИИ (&ГруппыТоваров)
| ИНАЧЕ ИСТИНА
| КОНЕЦ";
Запрос.УстановитьПараметр("ИспользоватьОтбор", ЕстьОтбор);
Запрос.УстановитьПараметр("ГруппыТоваров", ГруппыНоменклатуры.ВыгрузитьЗначения());
Обратите внимание на конструкцию в секции ГДЕ. Если флаг &ИспользоватьОтбор равен Ложь, выражение ВЫБОР возвращает ИСТИНА, и отбор по номенклатуре полностью игнорируется.
Важный нюанс при проверке Булево: Если существует вероятность, что в параметр &ИспользоватьОтбор может попасть значение не булева типа (например, НЕОПРЕДЕЛЕНО или NULL в результате работы сложных алгоритмов), безопаснее явно прописывать сравнение КОГДА &ИспользоватьОтбор = ИСТИНА. Это убережет запрос от аварийного завершения при неявных типах данных.
Хотя первый способ прост в реализации, логика с условием ВЫБОР ... ИНАЧЕ ИСТИНА в секции ГДЕ может негативно влиять на производительность при работе с большими объемами данных. Оптимизатор СУБД в некоторых случаях не может эффективно использовать индексы, что приводит к полному сканированию таблицы (Index Scan).
Более производительный подход — формировать условие ГДЕ в тексте запроса динамически, только когда отбор реально необходим.
Разберем пример с использованием метки подстановки и функции СтрЗаменить():
Запрос = Новый Запрос;
ТекстЗапроса =
"ВЫБРАТЬ
| ТоварыОстатки.Номенклатура КАК Номенклатура,
| ТоварыОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыОстатки
|&УсловиеОтбора";
Если ГруппыНоменклатуры.Количество() > 0 Тогда
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", "ГДЕ ТоварыОстатки.Номенклатура В ИЕРАРХИИ (&ГруппыТоваров)");
Запрос.УстановитьПараметр("ГруппыТоваров", ГруппыНоменклатуры.ВыгрузитьЗначения());
Иначе
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", "");
КонецЕсли;
Запрос.Текст = ТекстЗапроса;
При таком подходе в SQL-сервер отправляется максимально чистый и оптимизированный код without лишних ветвлений.
{ГДЕ ТоварыОстатки.Номенклатура В ИЕРАРХИИ (&ГруппыТоваров)}. Если параметр не задан, платформа автоматически удалит условие из итогового SQL-запроса.Справочники.Номенклатура.ПустаяСсылка() в оператор В ИЕРАРХИИ формально выполнится без ошибки, но не вернет все данные, а лишь отфильтрует элементы без родителя, что нарушит логику работы программы.