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