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