При разработке алгоритмов поиска элементов справочников разработчики часто сталкиваются с ситуацией, когда запрос возвращает лишние позиции. Типичный пример: при попытке найти элемент с кратким наименованием «0» система находит позиции вида «0,00 конденсатор», «01 Резистор» и другие похожие строки. Разберем по шагам, почему так происходит, как работает оператор ПОДОБНО и в каких случаях следует использовать строгое равенство.
Рассмотрим исходный фрагмент кода, вызвавший неожиданное поведение:
Запрос.УстановитьПараметр("ВыбНаим", Наименование + "%");
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
| И Номенклатура.Наименование ПОДОБНО &ВыбНаим";
Рез = Запрос.Выполнить().Выгрузить();
Выясним причину такого результата. В языке запросов 1С оператор ПОДОБНО предназначен для сравнения строк по маске (шаблону). Символ процента % является специальным подстановочным знаком и обозначает любую последовательность символов произвольной длины (включая нулевую длину). Когда к переменной поиска добавляется процент в конце (Наименование + "%"), запрос ищет строки, которые начинаются с указанного значения. Соответственно, при значении «0» маска принимает вид 0%, под которую подпадают абсолютно любые наименования, начинающиеся с нуля.
Если перед нами стоит задача найти элемент, чье наименование в точности совпадает со строкой поиска, использование оператора ПОДОБНО не требуется. В этом случае применяем стандартный оператор сравнения = (равно).
Посмотрим на пример правильного кода для строгого сопоставления:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
| И Номенклатура.Наименование = &ВыбНаим";
Запрос.УстановитьПараметр("ВыбНаим", Наименование);
Результат = Запрос.Выполнить().Выгрузить();
Разберем преимущества использования знака равенства:
Наименование в справочниках 1С входит в стандартный индекс. Сравнение через знак равенства позволяет СУБД использовать поиск по индексу (Index Seek), что выполняется значительно быстрее, чем сканирование при использовании шаблонов.= сопоставляет строки по правилам стандарта, при которых хвостовые пробелы могут автоматически компенсироваться, тогда как шаблонный поиск чувствителен к каждому знаку.Если все же требуется искать элементы по частичному совпадению, проанализируем варианты правильного формирования шаблонов для оператора ПОДОБНО:
&Наименование + "%". Например, при значении «Винт» найдутся «Винт М4», «Винтовой зажим»."%" + &Наименование. При значении «мм» найдутся «Кабель 10 мм», «Провод 2.5 мм»."%" + &Наименование + "%". Найдутся все записи, где искомая строка встречается в начале, середине или конце.Посмотрим на пример кода с динамическим формированием условия поиска по вхождению:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
| И Номенклатура.Наименование ПОДОБНО &ШаблонПоиска";
Запрос.УстановитьПараметр("ШаблонПоиска", "%" + Наименование + "%");
Выборка = Запрос.Выполнить().Выбрать();
Рассмотрим подробнее особенность, с которой часто сталкиваются при поиске артикулов и номенклатуры со сложными названиями. Оператор ПОДОБНО резервирует несколько служебных символов:
Если в искомом наименовании содержится, например, символ подчеркивания («Товар_1»), оператор воспримет его как шаблон. Чтобы СУБД искала эти знаки как обычные символы текста, в запросе необходимо использовать конструкцию СПЕЦСИМВОЛ. Разберем пример экранирования:
// Экранируем символ процента и подчеркивания тильдой
ИскомыйТекст = СтрЗаменить(Наименование, "~", "~~");
ИскомыйТекст = СтрЗаменить(ИскомыйТекст, "%", "~%");
ИскомыйТекст = СтрЗаменить(ИскомыйТекст, "_", "~_");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО &Маска СПЕЦСИМВОЛ ""~""";
Запрос.УстановитьПараметр("Маска", "%" + ИскомыйТекст + "%");
Если поиск выполняется не в цикле и не требует объединения с другими таблицами базы, проанализируем штатный метод менеджера объекта:
СсылкаНаЭлемент = Справочники.Номенклатура.НайтиПоНаименованию(Наименование, Истина);
Если Не СсылкаНаЭлемент.Пустая() Тогда
// Элемент успешно найден
КонецЕсли;
Обратим особое внимание на второй параметр метода НайтиПоНаименованию — ТочноеСоответствие. По умолчанию данный параметр равен Ложь. Если вызвать метод без второго параметра, платформа выполнит поиск именно по началу строки (аналогично ПОДОБНО &Наим + "%"). Для строгого совпадения передача флага Истина обязательна.
В тексте обсуждения возник вопрос: допустимо ли вызывать метод Запрос.УстановитьПараметр(...) до того, как задано свойство Запрос.Текст? Да, это полностью допустимо и не вызывает ошибок платформы 1С. Объект Запрос сохраняет параметры во внутренней коллекции. Синтаксический разбор текста, сопоставление переданных параметров и компиляция результирующего SQL-запроса происходят только в момент вызова метода Запрос.Выполнить(). Однако для читаемости и стандартизации программного кода рекомендуется сначала определять текст запроса, а затем заполнять его параметры.