В практике разработчика 1С часто возникает задача поиска дублирующихся данных. Это могут быть ошибочно введенные документы, которые имеют одинаковые дату, контрагента, договор и сумму. Поиск таких записей вручную в большом списке практически невозможен, поэтому эффективнее всего использовать механизм запросов. В данной статье мы подробно разберем, как реализовать выборку документов с одинаковыми параметрами, проанализируем различные подходы и выберем наиболее оптимальные с точки зрения производительности.
Самый очевидный способ, который часто первым приходит в голову — это соединить таблицу документов с самой собой по тем реквизитам, которые должны совпадать. Рассмотрим этот метод на примере документа РеализацияТоваровУслуг. Предположим, нам нужно найти документы, у которых совпадают ДоговорКонтрагента, Реквизит1 и Реквизит2, но при этом это разные физические документы (у них разные номера или ссылки).
Разберем пример кода, предложенный в обсуждении:
ВЫБРАТЬ
Реализация1.Ссылка КАК Документ1,
Реализация2.Ссылка КАК Документ2,
Реализация1.ДоговорКонтрагента
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация2
ПО Реализация1.ДоговорКонтрагента = Реализация2.ДоговорКонтрагента
И Реализация1.Реквизит1 = Реализация2.Реквизит1
И Реализация1.Реквизит2 = Реализация2.Реквизит2
И Реализация1.Ссылка <> Реализация2.Ссылка
Проанализируем этот запрос:
Документ.РеализацияТоваровУслуг дважды, присваивая ей разные псевдонимы (Реализация1 и Реализация2).ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Это означает, что в результат попадут только те записи, для которых нашлась пара по указанным условиям.ПО мы перечисляем реквизиты, по которым ищем совпадения.Реализация1.Ссылка <> Реализация2.Ссылка критически важно. Без него система сопоставит каждый документ сам с собой (ведь у документа А те же реквизиты, что и у документа А), и мы получим список всех документов в базе.При использовании условия Реализация1.Ссылка <> Реализация2.Ссылка мы столкнемся с проблемой "зеркальности". Если в базе есть два одинаковых документа (Док №1 и Док №2), запрос выдаст две строки:
Чтобы избежать этого и получить каждую пару только один раз, заменим условие неравенства на условие сравнения: Реализация1.Ссылка > Реализация2.Ссылка. Таким образом, в результат попадет только одна комбинация из пары.
Метод прямого соединения (Self-join) отлично работает на небольших объемах данных. Однако, если в таблице десятки тысяч документов, такое соединение может выполняться крайне медленно (СУБД придется перебирать огромное количество комбинаций). Проанализируем более профессиональный подход — использование группировок и временных таблиц.
Разобьем задачу на два этапа:
Рассмотрим реализацию этого алгоритма:
// Шаг 1: Находим ключи дублей
ВЫБРАТЬ
Реализация.ДоговорКонтрагента КАК ДоговорКонтрагента,
Реализация.Реквизит1 КАК Реквизит1,
Реализация.Реквизит2 КАК Реквизит2
ПОМЕСТИТЬ ВТ_КлючиДублей
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
СГРУППИРОВАТЬ ПО
Реализация.ДоговорКонтрагента,
Реализация.Реквизит1,
Реализация.Реквизит2
ИМЕЮЩИЕ
КОЛИЧЕСТВО(Реализация.Ссылка) > 1
;
// Шаг 2: Выбираем документы по найденным ключам
ВЫБРАТЬ
Реализация.Ссылка,
Реализация.ДоговорКонтрагента,
Реализация.Реквизит1,
Реализация.Реквизит2
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_КлючиДублей КАК Ключи
ПО Реализация.ДоговорКонтрагента = Ключи.ДоговорКонтрагента
И Реализация.Реквизит1 = Ключи.Реквизит1
И Реализация.Реквизит2 = Ключи.Реквизит2
УПОРЯДОЧИТЬ ПО
Реализация.ДоговорКонтрагента,
Реализация.Реквизит1
Почему этот способ лучше?
В первом запросе мы сразу отсекаем все уникальные документы, оставляя только "подозрительные" наборы данных. Вторая часть запроса просто вытягивает детальную информацию по этим наборам. Это значительно снижает нагрузку на дисковую подсистему и процессор сервера базы данных.
При составлении подобных запросов необходимо учитывать несколько важных технических моментов, специфичных для платформы 1С:
СГРУППИРОВАТЬ ПО, СОЕДИНЕНИЕ или УПОРЯДОЧИТЬ ПО. Если ваш Реквизит1 — это длинный текст, вам придется использовать функцию ПОДСТРОКА(Реквизит1, 1, 1000) для сравнения.ДоговорКонтрагента, запрос посчитает их дублями. Если это нежелательно, добавьте условие ГДЕ ДоговорКонтрагента <> ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка).Если вам нужно сравнивать документы по 10 и более полям, текст запроса станет громоздким, а его выполнение — неэффективным. В таких случаях профессиональным решением будет добавление в документ скрытого реквизита ХешСумма (тип Строка, длина 32-64). При записи документа программно вычисляйте хеш (например, через Криптография.СформироватьХеш или просто конкатенацией строк) и записывайте его в это поле. Тогда поиск дублей сведется к простейшему запросу по одному индексированному полю ХешСумма.
Выяснив причину возникновения дублей и выбрав подходящий метод запроса, вы сможете быстро навести порядок в информационной базе и предотвратить ошибки в учете.