Как настроить логическое условие при поиске в запросе 1С: по коду или наименованию?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Торговля и дистрибуция
← На главную

Часто при разработке на платформе 1С возникает необходимость гибкого поиска данных, когда алгоритм должен применять разные фильтры в зависимости от заполненности входящих параметров. Рассмотрим классическую задачу: нам нужно найти позицию в регистре сведений НоменклатураКонтрагентов. Если передан конкретный код (не равен нулю), поиск должен идти по коду. Если же код отсутствует (равен нулю), то искать необходимо по наименованию номенклатуры контрагента. Выясним причину, по которой стандартные подходы не всегда работают оптимально, и разберем по шагам несколько вариантов решения.

Решение через составное логическое условие ИЛИ

Самый простой способ организовать логический выбор прямо в тексте запроса — использовать комбинацию операторов ИЛИ и И вместе с проверкой самого параметра. Посмотрим на пример, который успешно решает исходную задачу:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
|    НоменклатураКонтрагентов.Номенклатура,
|    НоменклатураКонтрагентов.ХарактеристикаНоменклатуры
|ИЗ
|    РегистрСведений.НоменклатураКонтрагентов КАК НоменклатураКонтрагентов
|ГДЕ 
|    (&Код = 0 И НоменклатураКонтрагентов.Контрагент = &Контрагент)
|    ИЛИ  
|    (&Код <> 0 И НоменклатураКонтрагентов.НаименованиеНоменклатурыКонтрагента ПОДОБНО &НаименованиеНоменклатурыКонтрагента)";

Запрос.УстановитьПараметр("Контрагент", Контрагент);
Запрос.УстановитьПараметр("Код", Код);
Запрос.УстановитьПараметр("НаименованиеНоменклатурыКонтрагента", НаименованиеНоменклатурыКонтрагента);

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

Проанализируем этот код. Мы используем в связке операторы И. Если первое условие &Код = 0 выполняется, то система ищет только по контрагенту. Если же код заполнен (&Код <> 0), включается вторая часть условия с оператором ПОДОБНО.

Использование оператора ВЫБОР

Другой возможный синтаксический вариант — использование конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ внутри секции ГДЕ. Посмотрим, как это может выглядеть:


|ГДЕ
|    ВЫБОР КОГДА &Код = 0 
|        ТОГДА НоменклатураКонтрагентов.НаименованиеНоменклатурыКонтрагента ПОДОБНО &НаименованиеНоменклатурыКонтрагента
|    ИНАЧЕ 
|        НоменклатураКонтрагентов.Контрагент = &Контрагент
|        И НоменклатураКонтрагентов.КодНоменклатурыКонтрагента = &Код
|    КОНЕЦ

Несмотря на то, что этот код визуально понятен, мы настоятельно не рекомендуем использовать подобные выражения в секции ГДЕ. Они вызывают серьезные проблемы при выполнении на стороне СУБД. Разберем подробнее, почему так происходит.

Ограничения платформы и проблемы производительности

Оба приведенных выше способа имеют серьезные недостатки с точки зрения производительности базы данных. Проанализируем основные "узкие места":

  1. Проблема использования ИЛИ: Применение конструкции ИЛИ совместно с параметрами приводит к тому, что оптимизатор СУБД не может построить эффективный план выполнения. Система отказывается от оптимального поиска по индексам (Index Seek) и переходит к полному сканированию таблицы (Table/Index Scan). На больших таблицах или регистрах сведений это приведет к критическим торможениям.
  2. Несаргебальность условий с оператором ВЫБОР: Оператор ВЫБОР КОГДА внутри секции ГДЕ делает условие несаргебальным (Non-SARGable). Это означает, что индексы базы данных становятся полностью бесполезными, и СУБД вынуждена построчно вычислять это логическое выражение для каждой записи в таблице.
  3. Медленный поиск через ПОДОБНО: Поиск подстроки через ПОДОБНО очень сильно нагружает сервер. Если шаблон содержит знак процента в начале (например, "%Текст%"), индексы применить невозможно. Индексное (диапазонное) сканирование работает только тогда, когда строка ищется по строго заданному началу ("Текст%").

Оптимальные способы решения задачи

Чтобы избежать блокировок и медленной работы, рассмотрим наиболее правильные архитектурные подходы (Best Practices) для решения подобных задач в 1С:

  1. Динамическое формирование текста запроса:

    Самый оптимальный подход — программно собирать нужный текст перед вызовом метода Запрос.Выполнить(). Используйте конструкцию Если ... Тогда ... Иначе в коде модуля 1С. Таким образом, в СУБД уйдет простой запрос без лишних условий и операторов ИЛИ, который идеально использует нужный индекс базы данных.

    
    ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1 Номенклатура, ХарактеристикаНоменклатуры ИЗ РегистрСведений.НоменклатураКонтрагентов ГДЕ Контрагент = &Контрагент ";
    
    Если Код <> 0 Тогда
        Запрос.Текст = ТекстЗапроса + "И КодНоменклатурыКонтрагента = &Код";
    Иначе
        Запрос.Текст = ТекстЗапроса + "И НаименованиеНоменклатурыКонтрагента ПОДОБНО &Наименование";
    КонецЕсли;
    
  2. Последовательное выполнение двух простых запросов:

    Если нет желания работать с динамическим текстом, выполните два отдельных запроса. Сначала сделайте попытку поиска по коду. Если метод Выборка.Следующий() возвращает ложь (результат пустой), тогда выполняйте второй, независимый запрос с поиском по наименованию. На практике два легких запроса отрабатывают в разы быстрее одного громоздкого.

  3. Применение СКД или Построителя запросов:

    Для сложных вариативных фильтров можно задействовать СхемаКомпоновкиДанных или ПостроительЗапроса. Платформа 1С умеет сама "вырезать" из финального SQL-кода те условия, параметры которых не были переданы, сохраняя при этом высокую скорость работы.

  4. Замена ПОДОБНО на полнотекстовый поиск (ПТП):

    Если бизнес-задача требует постоянного поиска номенклатуры по обрывкам наименований, откажитесь от SQL-оператора ПОДОБНО. Настройте и используйте встроенный полнотекстовый поиск платформы. ПТП работает по специально подготовленным индексам и выполняет поиск фрагментов текста несоизмеримо быстрее.

← На главную