При разработке и доработке конфигураций на платформе 1С программисты часто сталкиваются с задачей поиска элемента справочника по определенному значению, например, по номеру телефона. Первое, что приходит на ум — использовать стандартный метод НайтиПоРеквизиту. Однако на практике он может возвращать пустую ссылку, даже если визуально в базе данных нужный элемент присутствует. Давайте вместе разберем эту проблему, проанализируем причины такого поведения системы и рассмотрим эффективные способы решения.
Рассмотрим стандартный вызов функции, который часто пишут разработчики:
Телефон = СокрЛП(ЭлементыФормы.ТаблицаКлючей.Область(НомерСтроки, 1).Текст);
НайденноеЗначение = Справочники.БазаТМ.НайтиПоРеквизиту("Телефон1", Телефон);
Если код выше не находит нужного контрагента или физическое лицо, проанализируем основные причины:
НайтиПоРеквизиту ищет абсолютное совпадение строки «один в один». Если вы ищете номер 9200467766, а в базе записано +7 (920) 046-77-66, 89200467766 или вообще несколько телефонов через точку с запятой (9200467766; 9200467766), метод вернет пустую ссылку. Это не поиск по подстроке.НайтиПоРеквизиту для него платформой запрещено.СокрЛП убирает пробелы по краям искомой строки, но если в базе данных строка сохранена с пробелами внутри или дополнена пробелами (в случае строк фиксированной длины), точное совпадение не сработает.Если мы выяснили, что в реквизите могут содержаться дополнительные символы, несколько номеров подряд или префиксы (например, +7 или 8), нам нужно искать подстроку. В 1С для этого необходимо использовать язык запросов и оператор ПОДОБНО.
Посмотрим на пример функции, которая корректно найдет элемент справочника, если искомый телефон содержится где-то внутри строки реквизита:
Функция НайтиНечтоПоТелефону(Телефон)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БазаТМ.Ссылка КАК Ссылка,
| БазаТМ.ИНН КАК ИНН
|ИЗ
| Справочник.БазаТМ КАК БазаТМ
|ГДЕ
| БазаТМ.Телефон1 ПОДОБНО &Телефон1";
// Оборачиваем искомый телефон в знаки процентов для поиска в любом месте строки
Запрос.УстановитьПараметр("Телефон1", "%" + Телефон + "%");
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Новый Структура("Элемент, ИНН", Выборка.Ссылка, Выборка.ИНН);
КонецЕсли;
Возврат Неопределено;
КонецФункции
В этом коде знаки процента % по краям параметра указывают СУБД, что искомая последовательность цифр может находиться в любом месте реквизита Телефон1. Это решает проблему, когда номера хранятся в формате 9200467766; 1234567890.
Рассмотрим важный нюанс. Использование оператора ПОДОБНО с процентом в начале строки ("%920...") приводит к так называемому Full Table Scan (полному сканированию таблицы). СУБД не может применить индексы при таком поиске и вынуждена перебирать каждую запись справочника. На базе с десятками тысяч элементов это вызовет заметные «тормоза».
Чтобы избежать этого, разберем несколько вариантов оптимизации.
Если в базе скопилось много «грязных» данных, а поиск по фрагментам жизненно необходим, включите и используйте механизм полнотекстового поиска (ПП). Он специально спроектирован для быстрого поиска по неточным текстовым совпадениям. Для программного обращения используйте методы менеджера ПолнотекстовыйПоиск.
Проанализируем корень проблемы: система страдает от того, что данные (телефоны) вносятся в базу в свободном и неструктурированном формате. Разберем по шагам правильный архитектурный подход:
+7 (920) 046-77-66, в базу должна сохраняться строка 9200467766.Если мы приведем базу к такому стандарту, то мы сможем снова использовать быстрый метод точного поиска или запрос по точному совпадению, который отработает за миллисекунды, так как сможет задействовать индексы СУБД.
Если нормализацию базы провести прямо сейчас невозможно, а искать нужно разные варианты кодов, мы можем модернизировать наш запрос, добавив условие ИЛИ. Рассмотрим пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БазаТМ.Ссылка КАК Ссылка
|ИЗ
| Справочник.БазаТМ КАК БазаТМ
|ГДЕ
| (БазаТМ.Телефон1 ПОДОБНО &ТелефонТочный
| ИЛИ БазаТМ.Телефон1 ПОДОБНО &ТелефонСВосьмеркой)";
// Очищаем введенный номер от лишнего программно (предположим, у нас есть для этого функция)
ЧистыйНомер = ОчиститьНомер(Телефон); // вернет 10 цифр, например 9200467766
Запрос.УстановитьПараметр("ТелефонТочный", "%" + ЧистыйНомер + "%");
Запрос.УстановитьПараметр("ТелефонСВосьмеркой", "%8" + ЧистыйНомер + "%");
Подводя итог, можно утверждать, что метод НайтиПоРеквизиту — отличный и быстрый инструмент, но только для структурированных и проиндексированных данных. Для поиска «грязных» текстовых строк применяйте запросы с ПОДОБНО, однако помните о производительности и всегда стремитесь к стандартизации хранения контактной информации.