При разработке механизмов подбора номенклатуры, поиска по артикулам, оригинальным номерам или номерам производителей разработчики часто сталкиваются с ситуацией, когда в базе данных строка содержит различные разделители (тире, точки, слэши, звездочки и пробелы), например: "5-5*99/01.23,001". Пользователь при этом вводит чистую последовательность символов: "59901230".
Стандартный оператор языка запросов ПОДОБНО "%59901230%" в таком случае не найдет совпадение, так как в исходном тексте между цифрами находятся знаки препинания. В языке запросов 1С отсутствуют встроенные функции для динамической очистки строк от произвольных символов или использования регулярных выражений. Разберем, как решить эту задачу на практике, и проанализируем различные подходы с точки зрения производительности.
Рассмотрим вариант, когда данные предварительно получены во временную таблицу значений, после чего выполняется их нормализация и фильтрация с помощью запроса. Для удаления всех нежелательных знаков воспользуемся функцией общего модуля СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими().
Разберем по шагам реализацию данного алгоритма:
"%".НомерОригинальный, НомерПроизводителя, НомерПоставщика).ПОДОБНО.Посмотрим на пример программного кода:
СписокСимволов = ",:)+-*}{[]-?#$%^&=._/\|(-!№;%? ";
// 1. Подготовка строки поиска
ОчищеннаяСтрока = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, ОригинальнаяСтрокаПоиска, "");
СтрокаПоискаСПроцентом = "%" + ОчищеннаяСтрока + "%";
// 2. Получение исходных данных и их нормализация
ТЗНоменклатур = Запрос.Выполнить().Выгрузить();
Для Каждого СтрокаТЗ Из ТЗНоменклатур Цикл
СтрокаТЗ.НомерОригинальный = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, СтрокаТЗ.НомерОригинальный, "");
СтрокаТЗ.НомерПроизводителя = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, СтрокаТЗ.НомерПроизводителя, "");
СтрокаТЗ.НомерПоставщика = СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, СтрокаТЗ.НомерПоставщика, "");
КонецЦикла;
// 3. Выполнение запроса с фильтрацией через ПОДОБНО
ЗапросТЗ = Новый Запрос;
ЗапросТЗ.Текст =
"ВЫБРАТЬ
| Таб.Номенклатура КАК Номенклатура,
| Таб.НомерОригинальный КАК НомерОригинальный,
| Таб.НомерПроизводителя КАК НомерПроизводителя,
| Таб.НомерПоставщика КАК НомерПоставщика
|ПОМЕСТИТЬ ВТДанные
|ИЗ
| &Данные КАК Таб
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТДанные.Номенклатура КАК Номенклатура,
| ВТДанные.НомерОригинальный КАК НомерОригинальный,
| ВТДанные.НомерПроизводителя КАК НомерПроизводителя,
| ВТДанные.НомерПоставщика КАК НомерПоставщика
|ИЗ
| ВТДанные КАК ВТДанные
|ГДЕ
| (ВТДанные.НомерОригинальный ПОДОБНО &СтрокаПоискаСПроцентом
| ИЛИ ВТДанные.НомерПроизводителя ПОДОБНО &СтрокаПоискаСПроцентом
| ИЛИ ВТДанные.НомерПоставщика ПОДОБНО &СтрокаПоискаСПроцентом)";
ЗапросТЗ.УстановитьПараметр("Данные", ТЗНоменклатур);
ЗапросТЗ.УстановитьПараметр("СтрокаПоискаСПроцентом", СтрокаПоискаСПроцентом);
СписокРезультат = ЗапросТЗ.Выполнить().Выгрузить().ВыгрузитьКолонку("Номенклатура");
Выясним причину, почему Способ 1 не подходит для высоконагруженных систем и больших справочников. Передача таблицы значений в запрос создает временные таблицы в базе данных СУБД, а обход сотен тысяч строк в цикле 1С приводит к высокой нагрузке на процессор. Кроме того, поиск по маске %...% исключает использование индексов СУБД (происходит полное сканирование таблицы — Table Scan).
Для оптимизации работы на больших объемах данных разберем архитектурно правильный подход:
АртикулОчищенный или НомерПоиска), для которого включается свойство Индексировать (или «Индексировать с доп. упорядочиванием»).ПередЗаписью настраивается автоматическое заполнение этого реквизита очищенной строкой без спецсимволов.ОчищенныйНомер.
// Пример заполнения реквизита в модуле объекта справочника Номенклатура
Процедура ПередЗаписью(Отказ)
СписокСимволов = ",:)+-*}{[]-?#$%^&=._/\|(-!№;%? ";
АртикулОчищенный = ВРег(СтроковыеФункцииКлиентСервер.ЗаменитьОдниСимволыДругими(СписокСимволов, Артикул, ""));
КонецПроцедуры
Проанализируем структуру условий в секции ГДЕ. Проверка нескольких полей через оператор ИЛИ (например, НомерОригинальный ПОДОБНО ... ИЛИ НомерПроизводителя ПОДОБНО ...) существенно затрудняет построение эффективного плана выполнения запроса в СУБД.
Для ускорения выполнения запроса рекомендуется разделять условия с помощью конструкции ОБЪЕДИНИТЬ:
ВЫБРАТЬ
Спр.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК Спр
ГДЕ
Спр.НомерОригинальныйОчищенный ПОДОБНО &СтрокаПоиска
ОБЪЕДИНИТЬ
ВЫБРАТЬ
Спр.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК Спр
ГДЕ
Спр.НомерПроизводителяОчищенный ПОДОБНО &СтрокаПоиска
Такой запрос разделяется СУБД на независимые операции, что обеспечивает более быструю выборку данных.
Итог: для разовых задач и небольших коллекций допустимо очищать строки в цикле и передавать ТаблицуЗначений в запрос с оператором ПОДОБНО. Для постоянной и быстрой работы в рабочей базе следует создавать отдельные проиндексированные реквизиты с нормализованными данными при записи объектов.