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