Как ускорить проверку дублей контактной информации в справочнике Партнеры?

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

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

Разберем, почему исходный запрос выполняется долго, какие ограничения СУБД и платформы 1С на это влияют, и рассмотрим по шагам способы оптимизации этой проверки.

Анализ проблемы исходного запроса

Посмотрим на типовой запрос, который разработчики часто используют для поиска дубликатов:


ВЫБРАТЬ
    ПартнерыКонтактнаяИнформация.Тип,
    ПартнерыКонтактнаяИнформация.Представление
ИЗ
    Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
ГДЕ
    ПартнерыКонтактнаяИнформация.Тип = &Тип
    И ПартнерыКонтактнаяИнформация.Представление = &Представление

Выясним причину, почему такой запрос работает медленно даже на объеме в 3 000–4 000 партнеров:

  1. Отсутствие подходящего индекса: реквизит Представление в контактной информации обычно имеет длину Строка(500) или является строкой неограниченной длины. По умолчанию такие поля не индексируются СУБД. В результате при каждом вызове СУБД вынуждена выполнять полное сканирование всей таблицы (Table Scan), сравнивая длинные строки побайтово.
  2. Сравнение ненормализованных данных: поле Представление содержит произвольно отформатированный текст (например, +7 (999) 000-11-22, 89990001122 или 8-999-000-11-22). Прямое сравнение на равенство часто пропускает реальные дубликаты из-за разницы в символах форматирования.
  3. Избыточность выборки: запрос считывает все записи, удовлетворяющие условию, хотя для валидации достаточно выяснить факт наличия хотя бы одного совпадения.
  4. Отсутствие исключения текущего элемента: при перезаписи существующего партнера запрос найдет его же собственные контактные данные, что вызовет ложную блокировку записи.

Способ 1. Оптимизация самого текста запроса

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


ВЫБРАТЬ ПЕРВЫЕ 1
    1 КАК ЕстьДубль
ИЗ
    Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКИ
ГДЕ
    ПартнерыКИ.Ссылка <> &ТекущаяСсылка
    И ПартнерыКИ.Тип = &Тип
    И ПартнерыКИ.Представление = &Представление

Конструкция ВЫБРАТЬ ПЕРВЫЕ 1 позволяет СУБД прервать чтение таблицы сразу же после обнаружения первого совпадения, не дожидаясь полного прохода по всем строкам.

Важный момент: если в конфигурации включен механизм RLS (ограничение доступа на уровне записей), проверка перед записью может замедляться неявными проверками прав. В таком случае выполнение служебной проверки следует обернуть в привилегированный режим:


УстановитьПривилегированныйРежим(Истина);
Результат = Запрос.Выполнить();
УстановитьПривилегированныйРежим(Ложь);

Если Не Результат.Пустой() Тогда
    // Обработка обнаружения дубля
КонецЕсли;

Способ 2. Поиск по специализированным полям БСП вместо представления

В типовых конфигурациях 1С, использующих Библиотеку стандартных подсистем (БСП), контактная информация содержит не только визуальное представление, но и нормализованные поля: НомерТелефона (очищенный от скобок и дефисов) и АдресЭлектроннойПочты.

Разберем преимущества этого подхода:

  1. Нормализованные поля имеют небольшую фиксированную длину (например, 20 или 50 символов).
  2. Для реквизита НомерТелефона в метаданных можно установить свойство Индексировать (или Индексировать с доп. упорядочиванием).
  3. СУБД моментально находит совпадение по индексу (Index Seek) за доли миллисекунды, полностью исключая сканирование таблицы.

Посмотрим на пример запроса по индексированному полю номера телефона:


ВЫБРАТЬ ПЕРВЫЕ 1
    1
ИЗ
    Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКИ
ГДЕ
    ПартнерыКИ.Ссылка <> &ТекущаяСсылка
    И ПартнерыКИ.НомерТелефона = &ОчищенныйНомерТелефона

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

Способ 3. Вынос контактной информации в отдельный регистр сведений

Если в конфигурации контактная информация хранится в табличной части справочника, то при росте базы обращение к ней создает дополнительную нагрузку на таблицы самого объекта. Наиболее масштабируемое решение — использовать регистр сведений.

Разберем архитектуру такого решения:

  1. Создаем непериодический регистр сведений (например, ИспользованныеКонтакты).
  2. В качестве измерений задаем:
    • ТипКонтакта (ПеречислениеСсылка.ТипыКонтактнойИнформации);
    • НормализованноеЗначение (Строка ограниченной длины, например 100 символов, с установленным свойством индексации);
    • Партнер (СправочникСсылка.Партнеры).
  3. При записи партнера обновляем записи в этом регистре сведений.
  4. Перед записью выполняем проверку наличия записи через менеджер регистра сведений или простой индексный запрос.

Посмотрим на пример проверки через срез или прямой отбор регистра:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	РегистрКонтактов.Партнер
    |ИЗ
    |	РегистрСведений.ИспользованныеКонтакты КАК РегистрКонтактов
    |ГДЕ
    |	РегистрКонтактов.ТипКонтакта = &ТипКонтакта
    |	И РегистрКонтактов.НормализованноеЗначение = &НормализованноеЗначение
    |	И РегистрКонтактов.Партнер <> &ТекущийПартнер";

Запрос.УстановитьПараметр("ТипКонтакта", ТипКонтакта);
Запрос.УстановитьПараметр("НормализованноеЗначение", ОчищенныйКонтакт);
Запрос.УстановитьПараметр("ТекущийПартнер", Ссылка);

Если Не Запрос.Выполнить().Пустой() Тогда
    ВызватьИсключение "Партнер с такими контактными данными уже существует!";
КонецЕсли;

Такой подход гарантирует мгновенную проверку даже при миллионах записей, так как поиск выполняется строго по кластерному индексу измерений регистра сведений.

← На главную