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

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

При работе с запросами в платформе 1С:Предприятие разработчики часто сталкиваются с необходимостью отбора или исключения записей по текстовым соответствиям. Типичная ошибка начинающих специалистов — попытка использовать стандартные операторы сравнения (такие как = или <>) вместе с подстановочными знаками процента %. В результате запрос либо возвращает пустой результат, либо превращается в огромный, нечитаемый и непроизводительный код.

В этой статье мы подробно разберем, почему простые операторы сравнения не работают с масками поиска, как правильно использовать оператор ПОДОБНО, а также проанализируем влияние таких фильтров на производительность базы данных и рассмотрим лучшие альтернативные решения.

Анализ типичной ошибки: почему сравнение со знаком "%" не работает

Давайте посмотрим на фрагмент кода, с которым изначально обратился автор темы:


И НЕ Номенклатура.ВидНоменклатуры = "%Свинина%"
И Номенклатура.Наименование <> "%Свинина%"

В данном случае разработчик ожидал, что платформа воспримет символ процента % как маску, означающую «любое количество произвольных символов» (как это делается в SQL-операторе LIKE). Однако при использовании операторов равенства = или неравенства <> система ищет точное совпадение строки. Это значит, что запрос попытается найти в базе данных номенклатуру, наименование которой буквально начинается с символа процента и им же заканчивается — например, товар с названием "%Свинина%". Разумеется, таких позиций в реальной базе нет, и условие попросту не отрабатывает.

Чтобы задействовать механизм поиска по шаблону (маске), в языке запросов 1С предусмотрен специальный оператор — ПОДОБНО.

Решение: Использование оператора ПОДОБНО

Давайте разберем по шагам, как переписать громоздкое и нерабочее условие автора в элегантный и лаконичный вид. Оператор ПОДОБНО сопоставляет строку с шаблоном, в котором символ процента % означает последовательность любых символов произвольной длины, а символ подчеркивания _ — один произвольный символ.

Для того чтобы исключить из результата все позиции, содержащие в названии слово «свинина» в любом месте строки (в начале, в середине или в конце), нам достаточно написать следующее условие:


ГДЕ
    НЕ Номенклатура.Наименование ПОДОБНО "%свинин%"

Обратите внимание: мы сократили искомое слово до корня «свинин». Это позволит нам автоматически отсечь не только слово «Свинина», но и его различные словоформы: «свинины», «свиная», «свиной» и т.д. Нам больше не нужно писать десятки строк условий на каждый возможный падеж или прилагательное.

Важный нюанс: Регистронезависимость поиска в 1С

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

Выясним причину, почему в этом нет необходимости. В подавляющем большинстве случаев базы данных 1С (работающие под управлением MS SQL Server, PostgreSQL или IBM DB2) настроены на регистронезависимое сравнение строк (так называемый Collation с параметром CI — Case Insensitive, например, Cyrillic_General_CI_AS).

Это означает, что для СУБД буквы «С», «с» и даже «С» в верхнем регистре абсолютно идентичны при сравнении. Нам достаточно написать одно условие в нижнем регистре: ПОДОБНО "%свинин%", и база данных успешно отфильтрует строки с текстом «Свинина», «СВИНИНА», «свинина» или «сВиНиНа».

Проблема производительности: Почему маска "%...%" замедляет работу СУБД

Хотя конструкция ПОДОБНО "%свинин%" проста в написании и решает задачу, у нее есть серьезная техническая проблема, связанная с производительностью системы.

Проанализируем, как СУБД выполняет этот запрос. В базе данных для быстрого поиска по текстовым полям существуют индексы. Если бы наш шаблон поиска начинался с конкретных символов, а процент стоял только в конце — например, ПОДОБНО "Свин%" — СУБД смогла бы применить быстрый поиск по индексу (Index Seek). Она бы мгновенно перешла к разделу индекса на букву «С» и выбрала нужные записи.

Но когда символ процента % стоит в самом начале шаблона ("%свинин%"), СУБД не может определить, с какого символа начинается строка. В этом случае индекс становится бесполезным. Системе приходится выполнять полное сканирование таблицы (Table Scan или Index Scan), то есть физически считывать с диска абсолютно все строки справочника номенклатуры и проверять их на соответствие шаблону. Если в вашей базе сотни тысяч или миллионы товаров, такой запрос будет выполняться крайне медленно, создавая колоссальную нагрузку на сервер.

Альтернативные и более быстрые способы фильтрации

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

Способ 1. Использование механизма полнотекстового поиска (ППД)

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

Рассмотрим пример реализации на встроенном языке:


// Шаг 1. Находим все ссылки на номенклатуру со словом "свинина" через ППД
СписокПоиска = ПолнотекстовыйПоиск.СоздатьСписок("свинина", 1000);
СписокПоиска.ПолучитьСледующуюПорцию();

МассивИсключений = Новый Массив;
Для Каждого СтрокаРезультата Из СписокПоиска Цикл
    Если ТипЗнч(СтрокаРезультата.Значение) = Тип("СправочникСсылка.Номенклатура") Тогда
        МассивИсключений.Добавить(СтрокаРезультата.Значение);
    КонецЕсли;
КонецЦикла;

// Шаг 2. Передаем массив исключений в запрос
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Наименование КАК Наименование
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	НЕ Номенклатура.Ссылка В (&МассивИсключений)";

Запрос.УстановитьПараметр("МассивИсключений", МассивИсключений);
РезультатЗапроса = Запрос.Выполнить();

Этот метод отработает за миллисекунды, так как СУБД будет делать отбор по уникальным идентификаторам (UUID) ссылок, что является максимально быстрой операцией.

Способ 2. Использование дополнительных реквизитов и свойств

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

Правильным решением будет создание дополнительного реквизита для номенклатуры (например, булево свойство СодержитСвинину или классификатор видов мяса). В таком случае запрос будет выглядеть максимально просто и выполняться мгновенно:


ГДЕ
    Номенклатура.СодержитСвинину = ЛОЖЬ

Обращение «через точку» к реквизитам ссылочного типа

В исходном запросе автора присутствовала еще одна концептуальная ошибка:

И Номенклатура.ВидНоменклатуры <> "Пищевые добавки"

Поле ВидНоменклатуры имеет тип данных СправочникСсылка.ВидыНоменклатуры, а не является обычной строкой. Сравнение ссылки со строковым литералом напрямую приведет либо к ошибке несовместимости типов, либо к неверному результату.

Чтобы исправить это, можно обратиться к наименованию вида номенклатуры через точку: Номенклатура.ВидНоменклатуры.Наименование <> "Пищевые добавки". Однако помните, что такое обращение «через точку» в запросе порождает скрытое левое соединение (Left Join) в результирующем SQL-запросе, что также может снижать общую скорость выполнения. Лучшей практикой является передача конкретной ссылки в параметры запроса:


ГДЕ
    Номенклатура.ВидНоменклатуры <> &ИсключаемыйВид

Расширенные возможности оператора ПОДОБНО

В завершение разберем полезные спецсимволы оператора ПОДОБНО, которые могут пригодиться вам при решении более сложных задач фильтрации текстов:

  1. Квадратные скобки [...] — позволяют перечислить список или диапазон возможных символов в одной позиции. Например, шаблон "[Сс]вин%" найдет строки, начинающиеся строго на заглавную или строчную букву «с». Также можно задавать числовые диапазоны: "Товар [0-9]".
  2. Отрицание в квадратных скобках [^...] — позволяет исключить определенные символы на конкретной позиции. Шаблон "Товар [^0-9]" найдет позиции, где после слова «Товар » идет любой символ, кроме цифры.
  3. Конструкция СПЕЦСИМВОЛ — необходима, если вам нужно найти реальный символ процента или подчеркивания в тексте. Например, если нам нужно найти номенклатуру, содержащую текст "15%", мы должны экранировать символ процента с помощью управляющего символа:

// Пример использования экранирования спецсимволов
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Наименование
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	Номенклатура.Наименование ПОДОБНО ""%15~%%"" СПЕЦСИМВОЛ ""~""";

В этом примере символ ~ объявлен как спецсимвол, поэтому следующая за ним конструкция ~% воспринимается системой как обычный текстовый знак процента, а не как маска поиска.

Используйте рассмотренные методики составления запросов, всегда помните о производительности СУБД и старайтесь избегать лишнего дублирования кода там, где за вас может отработать логика платформы 1С!

← На главную