В практике разработки на платформе 1С:Предприятие 8 часто возникает задача создания универсальных функций для получения данных. Основная сложность заключается в реализации гибких фильтров: когда параметр может быть не заполнен (равен Неопределено), содержать одну ссылку или целый массив/список значений. При этом мы хотим, чтобы запрос работал эффективно и корректно обрабатывал ситуацию, когда фильтр должен быть проигнорирован (выборка по всем записям).
В данной статье мы подробно разберем несколько подходов к решению этой задачи, проанализируем их плюсы и минусы, а также затронем вопросы производительности на уровне СУБД.
Рассмотрим наиболее изящный с точки зрения компактности кода способ, который позволяет передавать в один и тот же параметр как массив значений, так и литерал Неопределено. Проанализируем конструкцию, предложенную в обсуждении:
ГДЕ
ВЫБОР
КОГДА НЕОПРЕДЕЛЕНО В (&Контрагент)
ТОГДА ИСТИНА
ИНАЧЕ ФТ_Долги.Клиент В (&Контрагент)
КОНЕЦ
Разберем по шагам, как это работает. Если в параметр &Контрагент передать массив, содержащий значение Неопределено, условие КОГДА сработает, и фильтр вернет ИСТИНА для всех строк таблицы. Если же передать массив со ссылками, сработает ветка ИНАЧЕ, где будет выполнена проверка на вхождение поля в список. Платформа 8.3 позволяет использовать оператор В даже для одиночных значений, автоматически преобразуя их в эффективное сравнение.
Важное замечание: такая конструкция может быть удобна для разработчика, но она не всегда оптимальна для планировщика запросов СУБД. Использование ВЫБОР внутри секции ГДЕ может помешать системе использовать индексы по полю Клиент.
Рассмотрим более традиционный и «прозрачный» метод, который часто рекомендуют для обеспечения стабильной производительности. Мы вводим дополнительный булевый параметр, который сообщает запросу, нужно ли применять фильтр.
В тексте запроса это выглядит так:
ГДЕ
(&ОтборОтключен ИЛИ ФТ_Долги.Клиент В (&Контрагент))
Теперь посмотрим на программную обвязку:
Если ЗначениеЗаполнено(Контрагент) Тогда
Запрос.УстановитьПараметр("ОтборОтключен", Ложь);
Запрос.УстановитьПараметр("Контрагент", Контрагент);
Иначе
Запрос.УстановитьПараметр("ОтборОтключен", Истина);
// Передаем пустой массив, чтобы избежать ошибки не заданного параметра
Запрос.УстановитьПараметр("Контрагент", Новый Массив);
КонецЕсли;
Проанализируем ситуацию с точки зрения SQL. Современные версии платформы 1С и оптимизаторы MS SQL Server/PostgreSQL умеют обрабатывать такие конструкции (так называемый «Parameter Sniffing»). Однако на огромных таблицах условие ИЛИ может привести к полному сканированию таблицы (Index Scan) вместо поиска по индексу (Index Seek).
Если производительность является критическим фактором, мы рекомендуем использовать динамическое изменение текста запроса. Это позволяет полностью исключить лишние условия из итогового SQL-запроса, отправляемого в СУБД.
Рассмотрим пример реализации с использованием подстановки через СтрЗаменить или шаблоны строк:
ТекстУсловия = "";
Если ЗначениеЗаполнено(Контрагент) Тогда
ТекстУсловия = "И ФТ_Долги.Клиент В (&Контрагент)";
Запрос.УстановитьПараметр("Контрагент", Контрагент);
КонецЕсли;
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ДополнительноеУсловие", ТекстУсловия);
Выясним причину, почему этот способ считается наиболее надежным для высоконагруженных систем:
ИЛИ или ВЫБОР.При реализации любого из вышеперечисленных методов необходимо учитывать особенности работы платформы 1С с типами NULL, НЕОПРЕДЕЛЕНО и пустыми коллекциями:
В (&Параметр) пустой массив, условие всегда будет ложным (ЛОЖЬ). Это важно помнить, так как если вы хотели «выбрать всё» при пустом массиве, без дополнительной логики запрос не вернет ни одной строки.НЕОПРЕДЕЛЕНО — это значение конкретного типа, а NULL — это отсутствие значения (например, при левом соединении). Оператор В корректно работает с НЕОПРЕДЕЛЕНО, но для проверки на NULL необходимо использовать конструкцию ЕСТЬ NULL.Неопределено. В этом случае проверка Клиент В (&Контрагент), где массив содержит Неопределено, отработает корректно и найдет такие записи.Для создания по-настоящему универсальных механизмов (например, в подсистемах отчетов) мы рекомендуем использовать объект СхемаЗапроса. Рассмотрим, почему это лучше простой замены строк:
СхемаЗапроса гарантирует, что вы не нарушите синтаксис запроса.ГДЕ и И, следя за структурой.&Отбор) случайно встречается в комментариях или строковых литералах внутри запроса.Подводя итог, можно сказать: для простых задач и небольших таблиц отлично подходит вариант с ВЫБОР или флаг-параметром. Однако для разработки универсальных инструментов в крупных базах данных следует отдавать предпочтение динамическому формированию текста запроса или работе со СхемаЗапроса, так как это гарантирует максимальную производительность на стороне сервера базы данных.