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