Как правильно найти дубликаты физических лиц в 1С с помощью запроса?

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

Рано или поздно любой разработчик 1С сталкивается с задачей поиска дубликатов в справочнике ФизическиеЛица. Эта проблема особенно актуальна для зарплатных и кадровых решений (таких как ЗУП или ERP), где задвоение карточек сотрудников ведет к критическим ошибкам в расчете налогов, взносов и формировании регламентированной отчетности.

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

Почему поиск дублей перебором в цикле — это плохая идея?

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

  1. Огромная нагрузка на СУБД: Вместо одного обращения к базе данных система выполняет сотни или тысячи мелких запросов. Это создает избыточный сетевой трафик и перегружает процессор сервера СУБД.
  2. Замедление работы: Время выполнения такой операции растет пропорционально объему данных. На реальной базе с десятками тысяч сотрудников обработка может зависнуть на несколько часов.
  3. Блокировки таблиц: Множественные последовательные обращения могут вызвать транзакционные блокировки, парализуя работу других пользователей в системе.

Правильный подход в парадигме 1С — это выполнение всей аналитической работы на стороне СУБД за один единый запрос.

Шаг 1. Группировка данных и использование конструкции ИМЕЮЩИЕ

Для того чтобы выявить дубликаты по определенному реквизиту (например, по представлению ФИО), мы должны сгруппировать записи справочника и отобрать только те группы, в которых количество элементов больше одного. Для этого в языке запросов 1С применяется секция ИМЕЮЩИЕ.

Рассмотрим базовый пример запроса, определяющий повторяющиеся ФИО:


ВЫБРАТЬ
    ФизическиеЛица.ФИО КАК ФИО,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ФизическиеЛица.Ссылка) КАК КоличествоДублей
ИЗ
    Справочник.ФизическиеЛица КАК ФизическиеЛица
СГРУППИРОВАТЬ ПО
    ФизическиеЛица.ФИО
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ФизическиеЛица.Ссылка) > 1

Проанализируем этот запрос подробнее:

Важная деталь: Этот запрос покажет нам список проблемных ФИО и количество дублей, но мы не сможем получить ссылки (Ссылка) конкретных элементов-двойников. Если мы попытаемся добавить поле ФизическиеЛица.Ссылка в выборку и в группировку, СУБД сгруппирует данные по уникальным ссылкам. В итоге количество для каждой строки всегда будет равно 1, и условие ИМЕЮЩИЕ вернет пустой результат. Как же нам получить сами дублирующиеся элементы?

Шаг 2. Использование временных таблиц для получения ссылок-дубликатов

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

  1. В первом пакете мы находим все дублирующиеся ФИО и помещаем их во временную таблицу (например, ВТ_Дубли).
  2. Во втором пакете мы соединяем полученную временную таблицу с исходным справочником ФизическиеЛица по полю ФИО. Это позволит «развернуть» сгруппированные данные обратно и получить все дублирующиеся ссылки.

Посмотрим на пример реализации такого запроса:


ВЫБРАТЬ
    ФизическиеЛица.ФИО КАК ФИО,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ФизическиеЛица.Ссылка) КАК КоличествоДублей
ПОМЕСТИТЬ ВТ_Дубли
ИЗ
    Справочник.ФизическиеЛица КАК ФизическиеЛица
СГРУППИРОВАТЬ ПО
    ФизическиеЛица.ФИО
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ФизическиеЛица.Ссылка) > 1
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Дубли.ФИО КАК ФИО,
    ФизическиеЛица.Ссылка КАК Ссылка,
    ФизическиеЛица.СтраховойНомерПФР КАК СНИЛС
ИЗ
    ВТ_Дубли КАК ВТ_Дубли
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица КАК ФизическиеЛица
        ПО ВТ_Дубли.ФИО = ФизическиеЛица.ФИО
УПОРЯДОЧИТЬ ПО
    ФИО

Обратите внимание на использование ВНУТРЕННЕГО СОЕДИНЕНИЯ вместо левого. Поскольку во временной таблице гарантированно лежат только те ФИО, у которых есть дубли в справочнике, внутреннее соединение отработает максимально быстро и вернет строго нужные строки со всеми ссылками.

Шаг 3. Поиск дубликатов по переданной таблице значений

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

Сначала мы передаем внешнюю таблицу значений в качестве параметра запроса (назовем параметр &ТаблицаДанных), предварительно протипизировав ее колонки. Поместим ее во временную таблицу.

Опасная ловушка при связи по «ИЛИ»: Часто разработчики пытаются связать таблицы условием с оператором ИЛИ (OR):


// ПРИМЕР КАК ДЕЛАТЬ НЕ РЕКОМЕНДУЕТСЯ!
ПО (ФизическиеЛица.СтраховойНомерПФР = ВТ_ВходныеДанные.СНИЛС
    ИЛИ ФизическиеЛица.ФИО = ВТ_ВходныеДанные.ФИО)

Использование ИЛИ в условиях соединения таблиц в СУБД приводит к катастрофическому падению производительности. Оптимизатор СУБД не может применить индексы и переходит к полному сканированию таблиц (Table Scan). Кроме того, если у вас в базе или во входящей таблице есть пустые значения СНИЛС, то все пустые СНИЛС свяжутся друг с другом, выдав огромное количество ложных дублей!

Как правильно сопоставлять данные по нескольким полям?

Для корректного и быстрого сопоставления по нескольким полям (ФИО и СНИЛС) лучше всего использовать разделение логики с помощью конструкции ОБЪЕДИНИТЬ ВСЕ. Мы разделяем поиск на две ветки: сначала ищем жесткие совпадения по уникальному СНИЛС (если он заполнен), а затем — по ФИО для тех записей, которые не удалось сопоставить по СНИЛС.

Давайте посмотрим на пример оптимизированного запроса сопоставления:


// Помещаем входную таблицу в запрос
ВЫБРАТЬ
    Таблица.ФИО КАК ВходнойФИО,
    Таблица.СНИЛС КАК ВходнойСНИЛС
ПОМЕСТИТЬ ВТ_ВходныеДанные
ИЗ
    &ТаблицаДанных КАК Таблица
;

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

ОБЪЕДИНИТЬ ВСЕ

// Поиск по ФИО (для записей, у которых нет СНИЛС или он не совпал)
ВЫБРАТЬ
    ВТ_ВходныеДанные.ВходнойФИО,
    ВТ_ВходныеДанные.ВходнойСНИЛС,
    ФизическиеЛица.Ссылка,
    "Совпадение по ФИО"
ИЗ
    ВТ_ВходныеДанные КАК ВТ_ВходныеДанные
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица КАК ФизическиеЛица
        ПО (ВТ_ВходныеДанные.ВходнойФИО = ФизическиеЛица.ФИО)
ГДЕ
    (ВТ_ВходныеДанные.ВходнойСНИЛС = "" 
     ИЛИ ВТ_ВходныеДанные.ВходнойСНИЛС <> ФизическиеЛица.СтраховойНомерПФР)

Этот подход гарантирует стабильно высокую скорость работы СУБД, так как оба соединения используют простые равенства по одному полю, под которые СУБД легко строит эффективные индексные планы.

Важные практические нюансы при поиске дублей

Для достижения 100% точности при поиске дубликатов физических лиц в реальных базах необходимо учитывать специфику хранения данных в типовых конфигурациях 1С:

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

← На главную