Как реализовать поиск подстроки в 1С без учета спецсимволов с помощью оператора ПОДОБНО?

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

При разработке механизмов подбора номенклатуры, поиска по артикулам, оригинальным номерам или номерам производителей разработчики часто сталкиваются с ситуацией, когда в базе данных строка содержит различные разделители (тире, точки, слэши, звездочки и пробелы), например: "5-5*99/01.23,001". Пользователь при этом вводит чистую последовательность символов: "59901230".

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

Способ 1. Очистка строк «на лету» в Таблице Значений перед выполнением запроса

Рассмотрим вариант, когда данные предварительно получены во временную таблицу значений, после чего выполняется их нормализация и фильтрация с помощью запроса. Для удаления всех нежелательных знаков воспользуемся функцией общего модуля СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими().

Разберем по шагам реализацию данного алгоритма:

  1. Формируем список нежелательных символов и удаляем их из пользовательской строки поиска, обрамляя результат знаками "%".
  2. Обходим полученную таблицу значений в цикле и очищаем анализируемые строковые поля (например, НомерОригинальный, НомерПроизводителя, НомерПоставщика).
  3. Передаем подготовленную таблицу значений в новый запрос в качестве параметра и применяем условие ПОДОБНО.

Посмотрим на пример программного кода:


СписокСимволов = ",:)+-*}{[]-?#$%^&=._/\|(-!№;%? ";

// 1. Подготовка строки поиска
ОчищеннаяСтрока = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, ОригинальнаяСтрокаПоиска, "");
СтрокаПоискаСПроцентом = "%" + ОчищеннаяСтрока + "%";

// 2. Получение исходных данных и их нормализация
ТЗНоменклатур = Запрос.Выполнить().Выгрузить();

Для Каждого СтрокаТЗ Из ТЗНоменклатур Цикл
    СтрокаТЗ.НомерОригинальный = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, СтрокаТЗ.НомерОригинальный, "");
    СтрокаТЗ.НомерПроизводителя = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, СтрокаТЗ.НомерПроизводителя, "");
    СтрокаТЗ.НомерПоставщика = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, СтрокаТЗ.НомерПоставщика, "");
КонецЦикла;

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

ЗапросТЗ.УстановитьПараметр("Данные", ТЗНоменклатур);
ЗапросТЗ.УстановитьПараметр("СтрокаПоискаСПроцентом", СтрокаПоискаСПроцентом);

СписокРезультат = ЗапросТЗ.Выполнить().Выгрузить().ВыгрузитьКолонку("Номенклатура");

Способ 2. Предварительный расчет и нормализация данных при записи (рекомендуемый)

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

Для оптимизации работы на больших объемах данных разберем архитектурно правильный подход:

  1. В справочник добавляется отдельный реквизит (например, АртикулОчищенный или НомерПоиска), для которого включается свойство Индексировать (или «Индексировать с доп. упорядочиванием»).
  2. В модуле объекта справочника в процедуре ПередЗаписью настраивается автоматическое заполнение этого реквизита очищенной строкой без спецсимволов.
  3. Если к одной номенклатуре привязано множество номеров (кросс-коды, аналоги), создается непериодический регистр сведений с проиндексированным измерением ОчищенныйНомер.
  4. Поиск выполняется прямым запросом к реальной таблице базы данных без выгрузки данных в память.

// Пример заполнения реквизита в модуле объекта справочника Номенклатура
Процедура ПередЗаписью(Отказ)
    
    СписокСимволов = ",:)+-*}{[]-?#$%^&=._/\|(-!№;%? ";
    АртикулОчищенный = ВРег(СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, Артикул, ""));
    
КонецПроцедуры

Способ 3. Оптимизация условий запроса без использования «ИЛИ»

Проанализируем структуру условий в секции ГДЕ. Проверка нескольких полей через оператор ИЛИ (например, НомерОригинальный ПОДОБНО ... ИЛИ НомерПроизводителя ПОДОБНО ...) существенно затрудняет построение эффективного плана выполнения запроса в СУБД.

Для ускорения выполнения запроса рекомендуется разделять условия с помощью конструкции ОБЪЕДИНИТЬ:


ВЫБРАТЬ
    Спр.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Спр
ГДЕ
    Спр.НомерОригинальныйОчищенный ПОДОБНО &СтрокаПоиска

ОБЪЕДИНИТЬ

ВЫБРАТЬ
    Спр.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Спр
ГДЕ
    Спр.НомерПроизводителяОчищенный ПОДОБНО &СтрокаПоиска

Такой запрос разделяется СУБД на независимые операции, что обеспечивает более быструю выборку данных.

Итог: для разовых задач и небольших коллекций допустимо очищать строки в цикле и передавать ТаблицуЗначений в запрос с оператором ПОДОБНО. Для постоянной и быстрой работы в рабочей базе следует создавать отдельные проиндексированные реквизиты с нормализованными данными при записи объектов.

← На главную