В процессе эксплуатации информационных систем, особенно при интеграции нескольких баз (CRM, Бухгалтерия, Торговля), неизбежно возникает проблема дублирования данных. Одной из самых сложных задач является не просто поиск дублей по наименованию, а определение так называемой «золотой записи» или эталона. Рассмотрим, как реализовать механизм поиска эталона и отделения его от дублей с помощью одного пакетного запроса, опираясь на сложные критерии: присутствие объекта в разных базах и полноту заполнения его реквизитов.
Проанализируем ситуацию: в самописном справочнике Контрагенты имеются записи, пришедшие из разных источников. У каждой записи есть реквизиты-идентификаторы (УИД) для каждой из баз: CRM, БП (Бухгалтерия), Торговля. Также введен показатель Вес — это число заполненных значимых реквизитов в карточке.
Разберем алгоритм выбора эталона, который нам необходимо внедрить в запрос:
Вес (наибольшая заполненность данных).Поскольку стандартный язык запросов 1С (до версии 8.3.20) не поддерживал оконные функции в полной мере, наиболее надежным способом решения является использование временных таблиц. Рассмотрим пошагово, как построить такой запрос.
Шаг 1: Сбор всех кандидатов и расчет их характеристик
Сначала выберем всех контрагентов, которые потенциально имеют дубли, и рассчитаем для них количество источников. Выясним причину популярности этого подхода: он позволяет сразу отсечь уникальные записи и работать только с группами дублей.
ВЫБРАТЬ
Контрагенты.Ссылка КАК Ссылка,
Контрагенты.НаименованиеПолное КАК НаименованиеПолное,
Контрагенты.Вес КАК Вес,
(ВЫБОР КОГДА Контрагенты.УИД_CRM <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ +
ВЫБОР КОГДА Контрагенты.УИД_БП <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ +
ВЫБОР КОГДА Контрагенты.УИД_Торговля <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) КАК КоличествоБаз,
ВЫБОР КОГДА Контрагенты.УИД_БП <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ КАК НаличиеБП
ПОМЕСТИТЬ ВТ_ДанныеКонтрагентов
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
НЕ Контрагенты.ЭтоГруппа
И Контрагенты.НаименованиеПолное В (
ВЫБРАТЬ
К.НаименованиеПолное
ИЗ
Справочник.Контрагенты КАК К
СГРУППИРОВАТЬ ПО
К.НаименованиеПолное
ИМЕЮЩИЕ
КОЛИЧЕСТВО(К.Ссылка) > 1
)
;
Шаг 2: Определение максимальных показателей в группе
Проанализируем ситуацию внутри каждой группы дублей. Нам нужно найти «лучшие» показатели по каждому критерию для каждого наименования. Посмотрим на пример группировки:
ВЫБРАТЬ
ВТ.НаименованиеПолное КАК НаименованиеПолное,
МАКСИМУМ(ВТ.НаличиеБП) КАК МаксБП,
МАКСИМУМ(ВТ.КоличествоБаз) КАК МаксБаз,
МАКСИМУМ(ВТ.Вес) КАК МаксВес
ПОМЕСТИТЬ ВТ_МаксимальныеПоказатели
ИЗ
ВТ_ДанныеКонтрагентов КАК ВТ
СГРУППИРОВАТЬ ПО
ВТ.НаименованиеПолное
;
Шаг 3: Итоговый выбор эталона и дублей
Теперь объединим данные. Выясним, какая запись соответствует всем критериям лидерства. Мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ, чтобы сопоставить текущие записи с максимальными значениями по группе. Важный момент: если даже после всех проверок остаются две абсолютно идентичные по весу и базам записи, мы выберем одну по внутреннему идентификатору Ссылка (используя МАКСИМУМ(Ссылка)), чтобы избежать появления двух эталонов для одного имени.
ВЫБРАТЬ
ВТ.НаименованиеПолное,
ВТ.Ссылка КАК Эталон
ПОМЕСТИТЬ ВТ_Эталоны
ИЗ
ВТ_ДанныеКонтрагентов КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксимальныеПоказатели КАК Макс
ПО ВТ.НаименованиеПолное = Макс.НаименованиеПолное
И ВТ.НаличиеБП = Макс.МаксБП
И ВТ.КоличествоБаз = Макс.МаксБаз
И ВТ.Вес = Макс.МаксВес
;
Если ваша платформа позволяет использовать современные возможности языка запросов, задачу можно решить значительно элегантнее. Проанализируем, как ROW_NUMBER() упрощает жизнь разработчику. Вместо трех временных таблиц мы можем использовать одну конструкцию ОБЛАСТЬ (PARTITION BY ...).
ВЫБРАТЬ
Контрагенты.Ссылка,
Контрагенты.НаименованиеПолное,
НОМЕР_СТРОКИ() НАД(
ПАРТИЦИЯ ПО Контрагенты.НаименованиеПолное
УПОРЯДОЧИТЬ ПО
(ВЫБОР КОГДА Контрагенты.УИД_БП <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) УБЫВ,
(ВЫБОР КОГДА Контрагенты.УИД_CRM <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ +
ВЫБОР КОГДА Контрагенты.УИД_БП <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ +
ВЫБОР КОГДА Контрагенты.УИД_Торговля <> "" ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) УБЫВ,
Контрагенты.Вес УБЫВ
) КАК ИндексПопулярности
ИЗ
Справочник.Контрагенты КАК Контрагенты
В данном случае запись, у которой ИндексПопулярности = 1, автоматически становится эталоном, а все остальные записи с тем же наименованием — дублями. Это решение исключает необходимость сложных соединений и работает существенно быстрее на больших объемах данных.
Рассмотрим несколько важных нюансов, которые помогут сделать работу с дублями безопасной:
НаименованиеПолное проиндексировано. В противном случае поиск дублей приведет к сканированию всей таблицы и блокировкам.НаименованиеДляПоиска.ОбщегоНазначения.НайтиИЗаменитьДубли(). После того как запрос определил «Эталон» и «Дубль», этот метод корректно перенесет все ссылки из документов на эталонную запись.ИНН или дате рождения, если это возможно.Таким образом, комбинируя расчет «веса» карточки и анализ источников данных, мы получаем мощный инструмент для очистки нормативно-справочной информации, который можно легко адаптировать под любые требования бизнеса.