Как реализовать необязательные параметры и отборы в запросах 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке запросов в платформе 1С:Предприятие часто возникает задача сделать отбор опциональным — например, накладывать фильтр по реквизиту только тогда, когда пользователь заполнил соответствующее поле на форме. В связи с этим у разработчиков возникает два ключевых вопроса: можно ли передавать в метод УстановитьПараметр() переменные, которых нет в тексте запроса, и как технически правильно реализовать необязательное условие в секции ГДЕ.

Разберем детально поведение платформы 1С и рассмотрим наиболее эффективные подходы к решению этой задачи.

Поведение платформы при установке параметров

Проанализируем базовые правила платформы 1С при работе с объектом Запрос:

  1. Параметр есть в тексте, но не установлен в коде. Если в тексте запроса объявлен параметр через символ & (например, &ПарамНаименование), но перед вызовом метода Выполнить() разработчик не вызвал УстановитьПараметр(), платформа сгенерирует исключительную ситуацию: «Не задано значение параметра...». В тексте запроса все объявленные параметры являются строго обязательными.
  2. Параметр установлен в коде, но отсутствует в тексте. Если мы передаем в метод УстановитьПараметр() имя и значение, которых нет в тексте запроса, платформа 1С проигнорирует этот вызов и выполнит запрос без ошибок.

Посмотрим на пример кода, демонстрирующий установку неиспользуемого параметра:


Запрос = Новый Запрос;
Запрос.Текст = 
	"ВЫБРАТЬ
	|	Номенклатура.Код КАК Код
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура";

// Параметра &ОтборНаименование в тексте нет, но код выполнится без ошибок:
Запрос.УстановитьПараметр("ОтборНаименование", Истина);

Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
	// Обработка результата
КонецЦикла;

Вариант 1. Условие через конструкцию ВЫБОР (не рекомендуется)

Иногда разработчики пытаются сделать параметр отбора условно-необязательным прямо внутри текста запроса, используя ветвление в блоке ГДЕ. Посмотрим на пример такой реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
	"ВЫБРАТЬ
	|	Номенклатура.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	ВЫБОР
	|		КОГДА &ОтборНаименование
	|			ТОГДА Номенклатура.Наименование = &ПарамНаименование
	|		ИНАЧЕ ИСТИНА
	|	КОНЕЦ";

Запрос.УстановитьПараметр("ОтборНаименование", ОтборНаименование);
Запрос.УстановитьПараметр("ПарамНаименование", ПарамНаименование);

Результат = Запрос.Выполнить();

Внимание: данный подход является антипаттерном. Использование конструкций ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ИСТИНА КОНЕЦ, а также условий с ИЛИ (например, ГДЕ &Параметр = ЗНАЧЕНИЕ(...) ИЛИ Поле = &Параметр) критически снижает производительность. Оптимизатор СУБД не может заранее построить эффективный план выполнения и задействовать индексы таблиц, что приводит к полному сканированию таблицы (Table Scan) на больших объемах данных.

Кроме того, сравнение параметров с Неопределено или NULL непосредственно в тексте запроса может приводить к ошибкам трансляции в SQL в зависимости от типов полей.

Вариант 2. Динамическая сборка текста запроса (рекомендуемый способ)

Разберем наиболее распространенный и производительный способ решения задачи на встроенном языке 1С — динамическое формирование секции ГДЕ в зависимости от заполненности условий.


Запрос = Новый Запрос;

ТекстЗапроса = 
	"ВЫБРАТЬ
	|	Номенклатура.Ссылка КАК Ссылка,
	|	Номенклатура.Наименование КАК Наименование
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура";

МассивУсловий = Новый Массив;

Если ЗначениеЗаполнено(ПарамНаименование) Тогда
	МассивУсловий.Добавить("Номенклатура.Наименование = &ПарамНаименование");
	Запрос.УстановитьПараметр("ПарамНаименование", ПарамНаименование);
КонецЕсли;

Если МассивУсловий.Количество() > 0 Тогда
	ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ГДЕ" + Символы.ПС + СтрСоединить(МассивУсловий, " И ");
КонецЕсли;

Запрос.Текст = ТекстЗапроса;
Результат = Запрос.Выполнить();

Такой подход гарантирует, что в СУБД уйдет только необходимый фрагмент условия, а оптимизатор сможет применить индекс по полю Наименование.

Вариант 3. Использование объекта СхемаЗапроса

Если требуется избежать ручной конкатенации строк, используем программный объект СхемаЗапроса. Он позволяет безопасно модифицировать структуру запроса, включая коллекцию отборов оператора выбора.


Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(
	"ВЫБРАТЬ
	|	Номенклатура.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура"
);

Если ЗначениеЗаполнено(ПарамНаименование) Тогда
	Оператор = Схема.ПакетЗапросов[0].Операторы[0];
	Оператор.Отбор.Добавить("Номенклатура.Наименование = &ПарамНаименование");
КонецЕсли;

Запрос = Новый Запрос;
Запрос.Текст = Схема.ПолучитьТекстЗапроса();

Если ЗначениеЗаполнено(ПарамНаименование) Тогда
	Запрос.УстановитьПараметр("ПарамНаименование", ПарамНаименование);
КонецЕсли;

Результат = Запрос.Выполнить();

Вариант 4. Использование Системы компоновки данных (СКД)

Для отчетов и динамических списков идеальным решением является Система компоновки данных. В СКД механизмы необязательных параметров и условий встроены на уровне платформы:

  1. В схеме компоновки данных достаточно объявить параметры или элементы пользовательских отборов.
  2. Если значение отбора не задано пользователем или флаг использования снят, СКД автоматически вырезает соответствующее условие из итогового SQL-запроса, формируя оптимальный план запроса на стороне СУБД.

Удобная пакетная установка параметров

Когда параметров много, удобно хранить их в объекте Структура и устанавливать в цикле:


СтруктураПараметров = Новый Структура;
Если ЗначениеЗаполнено(ПарамНаименование) Тогда
	СтруктураПараметров.Вставить("ПарамНаименование", ПарамНаименование);
КонецЕсли;

Для Каждого КлючИЗначение Из СтруктураПараметров Цикл
	Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
КонецЦикла;

Таким образом, для реализации гибких и производительных алгоритмов следует исключать конструкции ВЫБОР из секции ГДЕ и формировать текст условий динамически во встроенном языке либо средствами СКД.

← На главную