Как в запросе 1С использовать оператор ПОДОБНО для поиска по списку значений?

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

При разработке отчетов и механизмов поиска в платформе 1С:Предприятие программисты часто сталкиваются с классической задачей: необходимо найти данные, соответствующие списку текстовых шаблонов (масок). Стандартный язык запросов 1С предоставляет мощный оператор ПОДОБНО для поиска по одной строке-маске, но не позволяет напрямую написать конструкцию вида ГДЕ Поле ПОДОБНО В(&СписокШаблонов). Попытка решить эту задачу простым циклом с выполнением запроса на каждой итерации приводит к катастрофическому падению производительности системы.

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

Метод 1. Динамическое построение текста запроса (через ИЛИ)

Если нам заранее неизвестно количество элементов в списке поиска, но мы предполагаем, что этот список относительно небольшой (например, до 15–20 шаблонов), наиболее производительным решением будет динамическая сборка текста запроса в коде 1С.

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


// Предположим, у нас есть исходный МассивШаблонов с элементами вида "%Иван%", "%Петр%"
ТекстУсловий = Новый Массив;
Запрос = Новый Запрос;

Для Индекс = 0 По МассивШаблонов.Количество() - 1 Цикл
    ИмяПараметра = "ШаблонПоиска_" + Формат(Индекс, "ЧГ=0");
    
    // Формируем строковое условие для каждого шаблона
    ТекстУсловий.Добавить("Регистр.Измерение ПОДОБНО &" + ИмяПараметра);
    
    // Устанавливаем значение параметра запроса
    Запрос.УстановитьПараметр(ИмяПараметра, МассивШаблонов[Индекс]);
КонецЦикла;

// Базовый текст запроса
ТекстЗапроса = 
"ВЫБРАТЬ
|   Регистр.Измерение КАК Измерение,
|   Регистр.Ресурс КАК Ресурс
|ИЗ
|   РегистрСведений.РегистрСведений1 КАК Регистр";

// Добавляем динамическое условие ГДЕ, если массив шаблонов не пустой
Если ТекстУсловий.Количество() > 0 Тогда
    ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ГДЕ " + СтрСоединить(ТекстУсловий, " ИЛИ ");КонецЕсли;

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

Важный нюанс производительности: Использование оператора ПОДОБНО накладывает серьезные ограничения на работу оптимизатора СУБД (MS SQL, PostgreSQL и др.). Если ваш шаблон поиска начинается со знака процента (например, %Значение%), СУБД физически не сможет использовать существующие индексы по полю Измерение. В этом случае произойдет полное сканирование таблицы (Index Scan или Table Scan), что критично для больших таблиц. Если бизнес-логика позволяет искать строго по началу строки (например, шаблон вида Значение%), СУБД применит быстрый поиск по индексу (Index Seek).

Также помните, что слишком длинная цепочка условий ИЛИ (более 50-100 элементов) может перегрузить оптимизатор запросов СУБД, что приведет к построению неоптимального плана выполнения запроса.

Метод 2. Использование временных таблиц и соединения по ПОДОБНО

Если список шаблонов для поиска велик (десятки и сотни элементов), динамическое построение условий через ИЛИ становится невозможным. В этом случае мы можем загрузить список масок во временную таблицу и выполнить соединение основной таблицы с этой временной таблицей непосредственно по условию ПОДОБНО.

Рассмотрим пример реализации этого алгоритма:


// Создаем таблицу значений для передачи шаблонов в параметры запроса
ТаблицаШаблонов = Новый ТаблицаЗначений;
ТаблицаШаблонов.Колонки.Добавить("Шаблон", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(150)));

Для Каждого ШаблонИзСписка Из СписокШаблонов Цикл
    НоваяСтрока = ТаблицаШаблонов.Добавить();
    НоваяСтрока.Шаблон = "%" + ШаблонИзСписка + "%";
КонецЦикла;

Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТаблицаШаблонов", ТаблицаШаблонов);

// Описываем запрос с помещением шаблонов во ВТ и последующим соединением
Запрос.Текст = 
"ВЫБРАТЬ
|   ВходныеДанные.Шаблон КАК Шаблон
|ПОМЕСТИТЬ ВТ_Шаблоны
|ИЗ
|   &ТаблицаШаблонов КАК ВходныеДанные
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗЛИЧНЫЕ
|   Регистр.Измерение КАК Измерение,
|   Регистр.Ресурс КАК Ресурс
|ИЗ
|   РегистрСведений.РегистрСведений1 КАК Регистр
|       ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Шаблоны КАК ВТ_Шаблоны
|       ПО Регистр.Измерение ПОДОБНО ВТ_Шаблоны.Шаблон";

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

Внимание! Это крайне опасный метод с точки зрения производительности!

Проанализируем технические риски этого подхода. Поскольку условие связи таблиц содержит оператор ПОДОБНО, СУБД не может применить стандартные алгоритмы соединения (такие как Hash Match или Merge Join). Единственный доступный СУБД вариант выполнения такого соединения — это вложенные циклы (Nested Loops). СУБД будет вынуждена построчно перебирать каждую запись из основной таблицы и для каждой записи проводить полный поиск совпадений по всей временной таблице шаблонов.

Если в вашем регистре содержится 100 000 записей, а в списке поиска — 100 шаблонов, СУБД выполнит 10 000 000 операций сравнения строк. Это приведет к колоссальной нагрузке на процессор сервера СУБД, долгому времени выполнения запроса и жестким блокировкам таблиц.

Рекомендация: Используйте данный метод исключительно в тех случаях, когда вы абсолютно уверены, что и в основной таблице, и во временной таблице содержится минимальное количество записей (не более нескольких сотен в сумме).

Метод 3. Перенос логики на Систему компоновки данных (СКД)

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

Разберем, как это работает:

  1. При настройке схемы компоновки данных вы можете вынести поле Измерение в доступные поля отбора.
  2. В настройках отчета пользователь или программный код может установить вид сравнения «Содержит» (для поиска одного шаблона) или специальный вид сравнения «В списке по подобию» (если данный вид сравнения поддерживается версией вашей платформы и типом СУБД).
  3. СКД самостоятельно проанализирует структуру метаданных, индексы и сгенерирует наиболее безопасный и быстрый SQL-запрос под конкретную используемую СУБД (MS SQL Server, PostgreSQL, IBM DB2 или Oracle Database).

Важно: Стандарты разработки 1С и безопасность (Экранирование спецсимволов)

Проектируя поиск по шаблону, разработчики часто забывают о существовании служебных символов самого оператора ПОДОБНО. К таким символам относятся: процент (%), подчеркивание (_), квадратные скобки ([ и ]). Если конечный пользователь введет в строку поиска символ процента или подчеркивания, логика СУБД нарушится. На некоторых СУБД (особенно под управлением PostgreSQL) это может вызвать системные ошибки выполнения запроса или вернуть неверный массив данных.

Согласно стандартам разработки фирмы "1С" (требования 1С:ИТС), при работе с оператором ПОДОБНО необходимо соблюдать два важнейших правила:

  1. Никогда не собирайте строку шаблона прямо внутри текста запроса с помощью конкатенации (например, избегайте конструкций ПОДОБНО &Параметр + "%"). Шаблон должен передаваться в параметр запроса в полностью готовом виде.
  2. Всегда экранируйте спецсимволы, введенные пользователем. Для этого в кодовой базе Библиотеки стандартных подсистем (БСП) реализована специальная функция ОбщегоНазначения.СформироватьСтрокуДляПоискаВЗапросе().

Давайте посмотрим на пример правильного и безопасного кода, написанного по стандартам 1С:


// Исходная строка, которую ввел пользователь (она может содержать "%", "_" или другие спецсимволы)
СтрокаПользователя = "Иван_Петров";

// Безопасная обработка строки средствами БСП с автоматическим экранированием спецсимволов
БезопаснаяСтрока = ОбщегоНазначения.СформироватьСтрокуДляПоискаВЗапросе(СтрокаПользователя);

// Формируем полный шаблон поиска. Символы "%" по краям означают поиск подстроки
ШаблонПараметра = "%" + БезопаснаяСтрока + "%";

Запрос = Новый Запрос;
// В тексте запроса обязательно указываем ключевое слово СПЕЦСИМВОЛ и символ экранирования "~"
Запрос.Текст = 
"ВЫБРАТЬ
|   Регистр.Измерение КАК Измерение,
|   Регистр.Ресурс КАК Ресурс
|ИЗ
|   РегистрСведений.Reg1 КАК Регистр
|ГДЕ
|   Регистр.Измерение ПОДОБНО &ШаблонПоиска СПЕЦСИМВОЛ ""~""";

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

Альтернатива для высоконагруженных систем: Полнотекстовый поиск (ПСП)

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

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

← На главную