Почему не работает НайтиПоРеквизиту при поиске по номеру телефона в 1С и как это исправить?

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

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

Ограничения метода НайтиПоРеквизиту

Рассмотрим стандартный вызов функции, который часто пишут разработчики:


Телефон = СокрЛП(ЭлементыФормы.ТаблицаКлючей.Область(НомерСтроки, 1).Текст);
НайденноеЗначение = Справочники.БазаТМ.НайтиПоРеквизиту("Телефон1", Телефон);

Если код выше не находит нужного контрагента или физическое лицо, проанализируем основные причины:

  1. Поиск только точного совпадения. Метод НайтиПоРеквизиту ищет абсолютное совпадение строки «один в один». Если вы ищете номер 9200467766, а в базе записано +7 (920) 046-77-66, 89200467766 или вообще несколько телефонов через точку с запятой (9200467766; 9200467766), метод вернет пустую ссылку. Это не поиск по подстроке.
  2. Строки неограниченной длины. Если тип реквизита в справочнике установлен как «Строка неограниченной длины», использовать метод НайтиПоРеквизиту для него платформой запрещено.
  3. Невидимые символы и пробелы. Функция СокрЛП убирает пробелы по краям искомой строки, но если в базе данных строка сохранена с пробелами внутри или дополнена пробелами (в случае строк фиксированной длины), точное совпадение не сработает.
  4. Отсутствие индексации. Хотя это не влияет на сам факт нахождения (система все равно попытается найти элемент), без установленного свойства реквизита «Индексировать» поиск на больших объемах данных будет работать крайне медленно.

Решение проблемы: используем запросы и оператор ПОДОБНО

Если мы выяснили, что в реквизите могут содержаться дополнительные символы, несколько номеров подряд или префиксы (например, +7 или 8), нам нужно искать подстроку. В 1С для этого необходимо использовать язык запросов и оператор ПОДОБНО.

Посмотрим на пример функции, которая корректно найдет элемент справочника, если искомый телефон содержится где-то внутри строки реквизита:


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

В этом коде знаки процента % по краям параметра указывают СУБД, что искомая последовательность цифр может находиться в любом месте реквизита Телефон1. Это решает проблему, когда номера хранятся в формате 9200467766; 1234567890.

Проблема производительности при поиске по подстроке

Рассмотрим важный нюанс. Использование оператора ПОДОБНО с процентом в начале строки ("%920...") приводит к так называемому Full Table Scan (полному сканированию таблицы). СУБД не может применить индексы при таком поиске и вынуждена перебирать каждую запись справочника. На базе с десятками тысяч элементов это вызовет заметные «тормоза».

Чтобы избежать этого, разберем несколько вариантов оптимизации.

Вариант 1: Полнотекстовый поиск

Если в базе скопилось много «грязных» данных, а поиск по фрагментам жизненно необходим, включите и используйте механизм полнотекстового поиска (ПП). Он специально спроектирован для быстрого поиска по неточным текстовым совпадениям. Для программного обращения используйте методы менеджера ПолнотекстовыйПоиск.

Вариант 2: Нормализация данных (Лучшая практика)

Проанализируем корень проблемы: система страдает от того, что данные (телефоны) вносятся в базу в свободном и неструктурированном формате. Разберем по шагам правильный архитектурный подход:

  1. Очистка при вводе: При записи элемента справочника программно очищайте номер телефона от всех нецифровых символов (скобки, тире, пробелы, знаки плюса).
  2. Единый формат: Приводите номер к единому стандарту. Например, всегда храните 10 последних цифр (без ведущих 8 или +7). Если вводится +7 (920) 046-77-66, в базу должна сохраняться строка 9200467766.
  3. Разделение данных: Никогда не храните несколько номеров в одном текстовом поле через точку с запятой. Для множественных контактов в 1С обычно используется табличная часть или специализированный регистр сведений (как подсистема «Контактная информация» в типовых решениях БСП).

Если мы приведем базу к такому стандарту, то мы сможем снова использовать быстрый метод точного поиска или запрос по точному совпадению, который отработает за миллисекунды, так как сможет задействовать индексы СУБД.

Как искать с учетом погрешностей ввода (8 или +7)

Если нормализацию базы провести прямо сейчас невозможно, а искать нужно разные варианты кодов, мы можем модернизировать наш запрос, добавив условие ИЛИ. Рассмотрим пример:


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

Запрос.УстановитьПараметр("ТелефонТочный", "%" + ЧистыйНомер + "%");
Запрос.УстановитьПараметр("ТелефонСВосьмеркой", "%8" + ЧистыйНомер + "%");

Подводя итог, можно утверждать, что метод НайтиПоРеквизиту — отличный и быстрый инструмент, но только для структурированных и проиндексированных данных. Для поиска «грязных» текстовых строк применяйте запросы с ПОДОБНО, однако помните о производительности и всегда стремитесь к стандартизации хранения контактной информации.

← На главную