При разработке часто возникает задача отобрать строки из таблицы значений по частичному совпадению текста. Нередко разработчики передают таблицу значений в качестве параметра в запрос и накладывают условие с оператором ПОДОБНО. Однако на практике такой запрос может вернуть пустой результат, даже если искомая строка гарантированно присутствует в данных. Разберем по шагам, почему возникает эта ошибка, как правильно настроить параметры поиска и как оптимизировать архитектуру решения.
Проанализируем фрагмент кода из исходной ситуации. В тексте запроса используется оператор ПОДОБНО:
ГДЕ
(ВТДанные.НомерОригинальный ПОДОБНО &СтрокаПоискаСПроцентом
ИЛИ ВТДанные.НомерПроизводителя ПОДОБНО &СтрокаПоискаСПроцентом
ИЛИ ВТДанные.НомерПоставщика ПОДОБНО &СтрокаПоискаСПроцентом)
При этом в параметр передается значение переменной СтрокаПоискаБезСимволов:
ЗапросТЗ.УстановитьПараметр("СтрокаПоискаСПроцентом", СтрокаПоискаБезСимволов);
Оператор ПОДОБНО в языке запросов 1С выполняет поиск по шаблону. Если переданная строка не содержит спецсимволов маски (таких как знак процента %, означающий любую последовательность символов произвольной длины, или знак подчеркивания _ для одного произвольного символа), СУБД выполняет точное посимвольное сравнение всей строки. В результате частичный поиск не срабатывает.
Для поиска подстроки в любой части поля обязательно обрамляем искомую строку символами %:
ШаблонПоиска = "%" + СокрЛП(СтрокаПоискаБезСимволов) + "%";
ЗапросТЗ.УстановитьПараметр("СтрокаПоискаСПроцентом", ШаблонПоиска);
Обратим внимание на наименование переменной: СтрокаПоискаБезСимволов. Очень часто разработчики предварительно удаляют из строки ввода дефисы, точки, слэши или пробелы (например, превращая артикул «12-345/A» в «12345A»).
Выясним причину несовпадения: если в самой таблице значений артикулы хранятся в исходном виде (с разделителями), то оператор ПОДОБНО "%12345A%" никогда не найдет строку «12-345/A». Посимвольное вхождение отсутствует. Если требуется нечувствительный к спецсимволам поиск, выполнять его средствами стандартного запроса к временной таблице неэффективно, так как язык запросов 1С не поддерживает регулярные выражения и удаление произвольных символов «на лету» без сложных конструкций.
При передаче таблицы значений во временную таблицу СУБД критически важно, чтобы колонки таблицы значений имели явное описание типов. Если таблица значений создавалась программно без указания КвалификаторыСтроки или типы колонок определены как составные, платформа может непредсказуемо обработать оператор ПОДОБНО.
Посмотрим, как правильно создавать типизированную таблицу значений:
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Строка"));
ОписаниеТипаСтрока = Новый ОписаниеТипов(МассивТипов, , Новый КвалификаторыСтроки(100));
ТЗНоменклатур = Новый ТаблицаЗначений;
ТЗНоменклатур.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗНоменклатур.Колонки.Добавить("НомерОригинальный", ОписаниеТипаСтрока);
ТЗНоменклатур.Колонки.Добавить("НомерПроизводителя", ОписаниеТипаСтрока);
ТЗНоменклатур.Колонки.Добавить("НомерПоставщика", ОписаниеТипаСтрока);
Если в строках содержатся значения NULL или Неопределено (например, пустые реквизиты), результат операции ПОДОБНО вернет значение Ложь, и такие записи будут отфильтрованы.
Рассмотрим архитектурную сторону вопроса. Передача таблицы значений в запрос влечет за собой создание физической временной таблицы в СУБД (например, в базе tempdb на MS SQL Server), сериализацию данных и их последующую передачу по сети. Это приводит к лишней нагрузке на сервер.
В зависимости от того, откуда получены исходные данные, выберем один из двух оптимальных путей решения задачи:
ПОДОБНО в исходном запросе к метаданным. Это позволит СУБД отобрать только нужные строки на самом раннем этапе.Разберем пример эффективной фильтрации таблицы значений без повторного вызова запроса с помощью функции СтрНайти():
ПодстрокаПоиска = ВРег(СокрЛП(СтрокаПоиска));
// Создаем таблицу для результатов копированием структуры
РезультатПоиска = ТЗНоменклатур.СкопироватьКолонки();
Для Каждого СтрДанных Из ТЗНоменклатур Цикл
// Приводим поля к верхнему регистру для регистронезависимого сравнения
ТекстДляПоиска = ВРег(СтрДанных.НомерОригинальный + " "
+ СтрДанных.НомерПроизводителя + " "
+ СтрДанных.НомерПоставщика);
Если СтрНайти(ТекстДляПоиска, ПодстрокаПоиска) > 0 Тогда
ЗаполнитьЗначенияСвойств(РезультатПоиска.Добавить(), СтрДанных);
КонецЕсли;
КонецЦикла;
Такой подход выполняется исключительно в оперативной памяти сервера приложений, избавляет от накладных расходов на создание временных таблиц в СУБД и гарантирует корректный регистронезависимый поиск.