При работе с запросами в платформе 1С:Предприятие разработчики часто сталкиваются с необходимостью отбора или исключения записей по текстовым соответствиям. Типичная ошибка начинающих специалистов — попытка использовать стандартные операторы сравнения (такие как = или <>) вместе с подстановочными знаками процента %. В результате запрос либо возвращает пустой результат, либо превращается в огромный, нечитаемый и непроизводительный код.
В этой статье мы подробно разберем, почему простые операторы сравнения не работают с масками поиска, как правильно использовать оператор ПОДОБНО, а также проанализируем влияние таких фильтров на производительность базы данных и рассмотрим лучшие альтернативные решения.
Давайте посмотрим на фрагмент кода, с которым изначально обратился автор темы:
И НЕ Номенклатура.ВидНоменклатуры = "%Свинина%"
И Номенклатура.Наименование <> "%Свинина%"
В данном случае разработчик ожидал, что платформа воспримет символ процента % как маску, означающую «любое количество произвольных символов» (как это делается в SQL-операторе LIKE). Однако при использовании операторов равенства = или неравенства <> система ищет точное совпадение строки. Это значит, что запрос попытается найти в базе данных номенклатуру, наименование которой буквально начинается с символа процента и им же заканчивается — например, товар с названием "%Свинина%". Разумеется, таких позиций в реальной базе нет, и условие попросту не отрабатывает.
Чтобы задействовать механизм поиска по шаблону (маске), в языке запросов 1С предусмотрен специальный оператор — ПОДОБНО.
Давайте разберем по шагам, как переписать громоздкое и нерабочее условие автора в элегантный и лаконичный вид. Оператор ПОДОБНО сопоставляет строку с шаблоном, в котором символ процента % означает последовательность любых символов произвольной длины, а символ подчеркивания _ — один произвольный символ.
Для того чтобы исключить из результата все позиции, содержащие в названии слово «свинина» в любом месте строки (в начале, в середине или в конце), нам достаточно написать следующее условие:
ГДЕ
НЕ Номенклатура.Наименование ПОДОБНО "%свинин%"
Обратите внимание: мы сократили искомое слово до корня «свинин». Это позволит нам автоматически отсечь не только слово «Свинина», но и его различные словоформы: «свинины», «свиная», «свиной» и т.д. Нам больше не нужно писать десятки строк условий на каждый возможный падеж или прилагательное.
В исходном коде автора можно заметить дублирование условий для разных регистров символов (например, отдельно прописаны %Свинина% и %свинина%).
Выясним причину, почему в этом нет необходимости. В подавляющем большинстве случаев базы данных 1С (работающие под управлением MS SQL Server, PostgreSQL или IBM DB2) настроены на регистронезависимое сравнение строк (так называемый Collation с параметром CI — Case Insensitive, например, Cyrillic_General_CI_AS).
Это означает, что для СУБД буквы «С», «с» и даже «С» в верхнем регистре абсолютно идентичны при сравнении. Нам достаточно написать одно условие в нижнем регистре: ПОДОБНО "%свинин%", и база данных успешно отфильтрует строки с текстом «Свинина», «СВИНИНА», «свинина» или «сВиНиНа».
Хотя конструкция ПОДОБНО "%свинин%" проста в написании и решает задачу, у нее есть серьезная техническая проблема, связанная с производительностью системы.
Проанализируем, как СУБД выполняет этот запрос. В базе данных для быстрого поиска по текстовым полям существуют индексы. Если бы наш шаблон поиска начинался с конкретных символов, а процент стоял только в конце — например, ПОДОБНО "Свин%" — СУБД смогла бы применить быстрый поиск по индексу (Index Seek). Она бы мгновенно перешла к разделу индекса на букву «С» и выбрала нужные записи.
Но когда символ процента % стоит в самом начале шаблона ("%свинин%"), СУБД не может определить, с какого символа начинается строка. В этом случае индекс становится бесполезным. Системе приходится выполнять полное сканирование таблицы (Table Scan или Index Scan), то есть физически считывать с диска абсолютно все строки справочника номенклатуры и проверять их на соответствие шаблону. Если в вашей базе сотни тысяч или миллионы товаров, такой запрос будет выполняться крайне медленно, создавая колоссальную нагрузку на сервер.
Если производительность системы для вас критична, рассмотрим более правильные архитектурные подходы, которые позволяют избежать медленного полнотекстового сканирования в теле запроса.
В платформу 1С встроен оптимизированный механизм полнотекстового поиска, который работает через предварительно построенные поисковые индексы (аналогично поисковым системам). Мы можем сначала программно получить список неподходящих элементов, а затем передать их в запрос в качестве параметра исключения.
Рассмотрим пример реализации на встроенном языке:
// Шаг 1. Находим все ссылки на номенклатуру со словом "свинина" через ППД
СписокПоиска = ПолнотекстовыйПоиск.СоздатьСписок("свинина", 1000);
СписокПоиска.ПолучитьСледующуюПорцию();
МассивИсключений = Новый Массив;
Для Каждого СтрокаРезультата Из СписокПоиска Цикл
Если ТипЗнч(СтрокаРезультата.Значение) = Тип("СправочникСсылка.Номенклатура") Тогда
МассивИсключений.Добавить(СтрокаРезультата.Значение);
КонецЕсли;
КонецЦикла;
// Шаг 2. Передаем массив исключений в запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.Ссылка В (&МассивИсключений)";
Запрос.УстановитьПараметр("МассивИсключений", МассивИсключений);
РезультатЗапроса = Запрос.Выполнить();
Этот метод отработает за миллисекунды, так как СУБД будет делать отбор по уникальным идентификаторам (UUID) ссылок, что является максимально быстрой операцией.
Если бизнес-логика предприятия требует регулярного исключения определенных категорий товаров (например, отказ от работы со свининой на уровне компании или для конкретных клиентов), то фильтрация по текстовому наименованию — в принципе неверный подход. Наименование товара может измениться, в нем может быть допущена опечатка, и фильтр перестанет работать.
Правильным решением будет создание дополнительного реквизита для номенклатуры (например, булево свойство СодержитСвинину или классификатор видов мяса). В таком случае запрос будет выглядеть максимально просто и выполняться мгновенно:
ГДЕ
Номенклатура.СодержитСвинину = ЛОЖЬ
В исходном запросе автора присутствовала еще одна концептуальная ошибка:
И Номенклатура.ВидНоменклатуры <> "Пищевые добавки"
Поле ВидНоменклатуры имеет тип данных СправочникСсылка.ВидыНоменклатуры, а не является обычной строкой. Сравнение ссылки со строковым литералом напрямую приведет либо к ошибке несовместимости типов, либо к неверному результату.
Чтобы исправить это, можно обратиться к наименованию вида номенклатуры через точку: Номенклатура.ВидНоменклатуры.Наименование <> "Пищевые добавки". Однако помните, что такое обращение «через точку» в запросе порождает скрытое левое соединение (Left Join) в результирующем SQL-запросе, что также может снижать общую скорость выполнения. Лучшей практикой является передача конкретной ссылки в параметры запроса:
ГДЕ
Номенклатура.ВидНоменклатуры <> &ИсключаемыйВид
В завершение разберем полезные спецсимволы оператора ПОДОБНО, которые могут пригодиться вам при решении более сложных задач фильтрации текстов:
[...] — позволяют перечислить список или диапазон возможных символов в одной позиции. Например, шаблон "[Сс]вин%" найдет строки, начинающиеся строго на заглавную или строчную букву «с». Также можно задавать числовые диапазоны: "Товар [0-9]".[^...] — позволяет исключить определенные символы на конкретной позиции. Шаблон "Товар [^0-9]" найдет позиции, где после слова «Товар » идет любой символ, кроме цифры.СПЕЦСИМВОЛ — необходима, если вам нужно найти реальный символ процента или подчеркивания в тексте. Например, если нам нужно найти номенклатуру, содержащую текст "15%", мы должны экранировать символ процента с помощью управляющего символа:
// Пример использования экранирования спецсимволов
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО ""%15~%%"" СПЕЦСИМВОЛ ""~""";
В этом примере символ ~ объявлен как спецсимвол, поэтому следующая за ним конструкция ~% воспринимается системой как обычный текстовый знак процента, а не как маска поиска.
Используйте рассмотренные методики составления запросов, всегда помните о производительности СУБД и старайтесь избегать лишнего дублирования кода там, где за вас может отработать логика платформы 1С!