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