Как определить, является ли номер телефона мобильным, в запросе 1С?

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

В процессе разработки бизнес-приложений на платформе 1С перед нами часто возникает задача классификации контактных данных. Одной из наиболее распространенных является задача определения типа телефонного номера (мобильный или городской). Это критически важно для интеграции с SMS-шлюзами, настройки автоматических рассылок, валидации данных при вводе и построения аналитической отчетности.

Основная сложность заключается в том, что телефонные номера в базах данных часто хранятся в неструктурированном виде, коды мобильных операторов имеют разную длину (от 2 до 5 символов в зависимости от страны и региона), а использование медленных операторов вроде ПОДОБНО (LIKE) в тяжелых запросах приводит к существенному падению производительности СУБД. В этой статье мы подробно разберем несколько эффективных архитектурных и программных решений этой задачи.

Почему простые решения в запросах не работают эффективно?

На первый взгляд, задача кажется тривиальной. Можно попытаться написать запрос с конструкцией ПОДОБНО или использовать соединение по условию ИЛИ. Давайте проанализируем, почему этот путь ведет к техническому тупику на больших базах данных:

  1. Низкая скорость СУБД. Использование оператора ПОДОБНО "%912%" или соединение таблиц по условию ПОДОБНО заставляет СУБД выполнять полное сканирование таблиц (Table Scan / Index Scan). Индексы в этом случае не работают, и запрос к справочнику с сотнями тысяч контрагентов будет выполняться недопустимо долго.
  2. Разная длина кодов. В России коды мобильных операторов обычно трехзначные (например, 912, 903), но существуют и четырехзначные префиксы (например, 4959, 8129 для прямых мобильных номеров). В странах СНГ (Беларусь, Казахстан) структура кодов и их длина также существенно отличаются. Соответственно, жестко завязаться на первые 3 символа номера нельзя.
  3. Административная сложность. Поддерживать актуальность жестко зашитых в коде или запросе префиксов невозможно — пулы номеров постоянно выделяются и обновляются регуляторами (такими как Минцифры РФ).

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

---

Решение 1. Денормализация данных (метод флага «ЭтоМобильный»)

Если во главу угла ставится максимальное быстродействие при чтении данных и выполнении отчетов, наилучшим решением является денормализация. Вместо того чтобы вычислять «мобильность» номера каждый раз динамически в тяжелых запросах, мы определим тип номера один раз — в момент его записи в базу данных.

Для этого добавим в справочник или табличную часть контактной информации булевский реквизит ЭтоМобильный. Проанализируем алгоритм его заполнения в модуле объекта при записи:


Процедура ПередЗаписью(Отказ)
    
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Проверяем, изменился ли номер телефона, чтобы не выполнять лишних вычислений
    Если ЭтоНовый() Или ЭтотОбъект.НомерТелефона <> ЭтотОбъект.Ссылка.НомерТелефона Тогда
        // Вызываем вспомогательную функцию для определения типа номера
        ЭтотОбъект.ЭтоМобильный = РаботаСТелефонией.ОпределитьЭтоМобильный(ЭтотОбъект.НомерТелефона, ЭтотОбъект.Страна);
    КонецЕсли;
    
КонецПроцедуры

Теперь наш запрос на получение мобильных телефонов становится невероятно простым, изящным и мгновенным, так как СУБД делает выборку по простому индексированному булеву полю:


ВЫБРАТЬ
    Контрагенты.Ссылка КАК Контрагент,
    Контрагенты.НомерТелефона КАК Номер
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    Контрагенты.ЭтоМобильный = ИСТИНА

Преимущества метода: Максимальная производительность СУБД. Индекс по полю ЭтоМобильный позволяет мгновенно выбирать нужные записи.
Недостатки: Требуется доработка структуры метаданных и проведение однократной обработки по заполнению нового реквизита для уже существующих записей в базе данных.

---

Решение 2. Динамическое определение через каскадные соединения (Cascade Joins)

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

Для этого создадим вспомогательный Регистр Сведений КлассификаторКодов со следующей структурой:

Поскольку длина кодов может варьироваться от 3 до 5 символов, мы предварительно «нарежем» анализируемый номер на префиксы разной длины во временной таблице, а затем выполним серию левых соединений по жесткому равенству:


ВЫБРАТЬ
    Телефоны.Ссылка КАК Ссылка,
    Телефоны.Страна КАК Страна,
    Телефоны.НомерТелефона КАК Номер,
    ПОДСТРОКА(Телефоны.НомерТелефона, 1, 3) КАК Префикс3,
    ПОДСТРОКА(Телефоны.НомерТелефона, 1, 4) КАК Префикс4,
    ПОДСТРОКА(Телефоны.НомерТелефона, 1, 5) КАК Префикс5
ПОМЕСТИТЬ ВТ_ПодготовленныеНомера
ИЗ
    Справочник.Контрагенты КАК Телефоны
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т.Ссылка КАК Контрагент,
    Т.Номер КАК НомерТелефона,
    ВЫБОР
        КОГДА ЕСТЬNULL(К5.ЭтоМобильный, ЛОЖЬ) ТОГДА ИСТИНА
        КОГДА ЕСТЬNULL(К4.ЭтоМобильный, ЛОЖЬ) ТОГДА ИСТИНА
        КОГДА ЕСТЬNULL(К3.ЭтоМобильный, ЛОЖЬ) ТОГДА ИСТИНА
        ИНАЧЕ ЛОЖЬ
    КОНЕЦ КАК ЭтоМобильный
ИЗ
    ВТ_ПодготовленныеНомера КАК Т
    // Соединяем по самому длинному префиксу (5 символов)
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлассификаторКодов КАК К5
        ПО К5.Код = Т.Префикс5 
        И К5.Страна = Т.Страна
    // Если по 5 символам не нашли, пробуем 4 символа
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлассификаторКодов КАК К4
        ПО К4.Код = Т.Префикс4 
        И К4.Страна = Т.Страна
        И К5.Код ЕСТЬ NULL
    // Если не нашли по 4, пробуем 3 символа
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлассификаторКодов КАК К3
        ПО К3.Код = Т.Префикс3 
        И К3.Страна = Т.Страна
        И К4.Код ЕСТЬ NULL 
        И К5.Код ЕСТЬ NULL

Почему это работает быстро? Операция ПОДСТРОКА в рамках одной таблицы выполняется СУБД чрезвычайно быстро. Все левые соединения происходят по точному равенству индексированных полей (оператор =), что позволяет планировщику СУБД использовать эффективный алгоритм Index Seek вместо сканирования таблиц. Использование условий И КX.Код ЕСТЬ NULL предотвращает лишние соединения, если более точное (длинное) совпадение уже было успешно найдено.

---

Решение 3. Использование реестра диапазонов номеров (Range Join)

В Российской Федерации и ряде других стран регуляторы публикуют официальные реестры выделенных емкостей (диапазонов) номеров. Например, реестр Минцифры (ранее Россвязи) содержит диапазоны в формате: Код (3 знака), Начало диапазона (7 знаков), Конец диапазона (7 знаков), Оператор.

Поскольку переносимость номеров (MNP) позволяет менять оператора связи, но **запрещает перенос номеров между мобильными (DEF) и фиксированными (ABC) сетями**, проверка по диапазонам гарантирует абсолютную точность определения мобильности номера.

Для реализации этого метода создадим Регистр Сведений РеестрДиапазоновНомеров:

Предположим, что номера телефонов в вашей базе очищены от кодов стран и хранятся в 10-значном формате (например, 9121234567). Напишем запрос с соединением по диапазонам:


ВЫБРАТЬ
    Абоненты.Ссылка КАК Ссылка,
    ПОДСТРОКА(Абоненты.НомерТелефона, 1, 3) КАК КодРегиона,
    ПОДСТРОКА(Абоненты.НомерТелефона, 4, 7) КАК ТелоНомера
ПОМЕСТИТЬ ВТ_Абоненты
ИЗ
    Справочник.ФизическиеЛица КАК Абоненты
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    А.Ссылка КАК ФизическоеЛицо,
    ЕСТЬNULL(Реестр.ЭтоМобильный, ЛОЖЬ) КАК ЭтоМобильный
ИЗ
    ВТ_Абоненты КАК А
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РеестрДиапазоновНомеров КАК Реестр
        ПО А.КодРегиона = Реестр.Код
        И А.ТелоНомера МЕЖДУ Реестр.НачалоДиапазона И Реестр.КонецДиапазона

Анализ эффективности: Данный метод превосходно решает проблему так называемых «прямых» мобильных номеров (городских номеров в коде 495 или 812, которые переадресованы на мобильные SIM-карты). Если конкретный диапазон в коде 495 числится за мобильным оператором (например, МТС или Теле2), система безошибочно определит его как мобильный. Соединение по условию МЕЖДУ (BETWEEN) по индексированным полям отрабатывает на СУБД за миллисекунды.

---

Решение 4. Архитектурный подход БСП (Библиотека стандартных подсистем)

Если ваша конфигурация разработана на базе современных решений от 1С, в ней гарантированно используется подсистема «Контактная информация» из БСП. Давайте проанализируем, как эта задача решается на уровне стандартов разработки 1С.

При вводе номера телефона БСП автоматически нормализует его и сохраняет в базу данных в формате XML или JSON (поле ЗначенияПолей). В этой структурированной строке уже разделены: код страны, код города, номер и добавочный. Нам не нужно парсить сырую строку самостоятельно!

Для работы с номерами рекомендуется использовать программные методы общего модуля УправлениеКонтактнойИнформацииКлиентСервер или УправлениеКонтактнойИнформацииСлужебный. Например, для интерактивной проверки или обработки при записи можно использовать следующий вызов:


// Пример разбора номера с помощью БСП
СтруктураНомера = УправлениеКонтактнойИнформацииКлиентСервер.ДетализироватьНомерТелефона(СтрокаНомера);
// На выходе получаем структуру с ключами: КодСтраны, КодГорода, Номер, Добавочный
КодГорода = СтруктураНомера.КодГорода;

Если БСП настроена корректно, вы можете опираться на типы контактной информации и классифицировать телефоны еще на этапе их ввода пользователем, исключая необходимость сложных вычислений в транзакционных запросах.

---

Какое решение выбрать?

Подведем итог и выберем оптимальную стратегию в зависимости от условий вашей задачи:

  1. Если у вас высоконагруженная система и критически важна скорость работы отчетов и рассылок — используйте Решение 1 (Денормализация с флагом). Это стандарт проектирования баз данных для обеспечения максимальной производительности.
  2. Если вам запрещено менять структуру таблиц, но данные номеров хорошо очищены — используйте Решение 2 (Каскадные соединения). Оно работает значительно быстрее, чем использование ПОДОБНО.
  3. Если вам необходима высочайшая точность, включая распознавание прямых городских номеров, принадлежащих сотовым операторам — используйте Решение 3 (Реестр диапазонов), регулярно обновляя базу диапазонов Минцифры.
← На главную