Как быстро найти дубли номенклатуры в базе большого объема и избежать нехватки памяти?

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

При работе с крупными справочниками (от сотен тысяч позиций) разработчики часто сталкиваются с задачей поиска дубликатов по наименованию. Попытка решить эту проблему «в лоб» через создание неоптимизированных временных таблиц и сложное соединение справочника с самим собой нередко приводит к аварийному завершению работы 1С с сообщением о нехватке памяти. Рассмотрим, почему падает производительность исходного алгоритма, и разберем по шагам правильные и быстрые варианты поиска дублей.

Анализ ошибок в исходном запросе

Проанализируем фрагмент запроса, вызывающий перегрузку сервера или клиента:


ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.НаименованиеПолное
ПОМЕСТИТЬ ВТТабл
ИЗ
    Справочник.Номенклатура КАК Номенклатура
;
ВЫБРАТЬ
    ВТТабл.НаименованиеПолное,
    ВТТабл.Ссылка
ИЗ
    ВТТабл КАК ВТТабл
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
        ПО ВТТабл.Ссылка.Код <> Номенклатура.Код
            И (Номенклатура.НаименованиеПолное = ВТТабл.НаименованиеПолное)
            И ВТТабл.Ссылка.Наименование <> Номенклатура.Наименование

Выясним главные причины критического потребления ресурсов:

  1. Неявные соединения через точку в секции соединения. Конструкции вида ВТТабл.Ссылка.Код и ВТТабл.Ссылка.Наименование заставляют платформу 1С генерировать дополнительные скрытые соединения LEFT JOIN к исходной таблице справочника, многократно усложняя план выполнения.
  2. Условия неравенства в секции ПО. Использование оператора <> не позволяет СУБД применить эффективные алгоритмы слияния (такие как Hash Match или Merge Join). СУБД вынуждена использовать вложенные циклы (Nested Loops) или формировать огромное декартово произведение, что моментально забивает базу tempdb и оперативную память.
  3. Избыточность выборки. Помещение всех 200 000+ строк во временную таблицу без фильтрации и последующее неоптимальное соединение заставляют систему обрабатывать миллионы бессмысленных связей.
  4. Неограниченная длина строк. Если реквизит НаименованиеПолное имеет тип неограниченной длины, СУБД тратит дополнительные ресурсы на операции сравнения, а в некоторых случаях платформа вовсе блокирует прямое сравнение и группировку без явного приведения типов.

Способ 1. Поиск дублей с помощью группировки и конструкции ИМЕЮЩИЕ

Самый простой, быстрый и эффективный способ получить список значений, которые повторяются более одного раза, — выполнить группировку непосредственно по наименованию и отсечь уникальные позиции условием ИМЕЮЩИЕ КОЛИЧЕСТВО(...) > 1. Рассмотрим этот пример:


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

Разберем важные нюансы этого запроса:

  1. Мы используем КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура.Ссылка), чтобы точно исключить возможные коллизии.
  2. Мы обязательно отсекаем группы (ЭтоГруппа = ЛОЖЬ), так как дубли среди папок справочника обычно не являются предметом анализа.
  3. Если сравнение выполняется по реквизиту НаименованиеПолное неограниченной длины, его необходимо ограничить с помощью функции языка запросов ВЫРАЗИТЬ(Номенклатура.НаименованиеПолное КАК СТРОКА(1000)).

Способ 2. Двухэтапная выборка с получением ссылок на элементы-дубли

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


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

ИНДЕКСИРОВАТЬ ПО
    НаименованиеПолное
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Номенклатура.Код КАК Код,
    Номенклатура.Наименование КАК Наименование,
    ВТ_ДублиНаименований.НаименованиеПолное КАК НаименованиеПолное
ИЗ
    ВТ_ДублиНаименований КАК ВТ_ДублиНаименований
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
        ПО ВТ_ДублиНаименований.НаименованиеПолное = ВЫРАЗИТЬ(Номенклатура.НаименованиеПолное КАК СТРОКА(500))
ГДЕ
    НЕ Номенклатура.ЭтоГруппа
УПОРЯДОЧИТЬ ПО
    ВТ_ДублиНаименований.НаименованиеПолное,
    Номенклатура.Код

Обратим внимание на ключевые особенности этого решения:

  1. Индексация временной таблицы. Команда ИНДЕКСИРОВАТЬ ПО НаименованиеПолное гарантирует, что СУБД построит индекс в базе tempdb, и второе соединение выполнится практически мгновенно без повторного полного сканирования таблицы.
  2. Внутреннее соединение только по равенству. В условии ПО используется строго знак =. Никаких проверок на неравенство кодов или наименований здесь быть не должно. Все элементы, попавшие во внутреннее соединение с предварительно отобранным списком, по определению являются дублями.
  3. Ограничение объема передаваемых данных. Вместо загрузки полного справочника во временную таблицу попадают исключительно дублирующиеся названия, что снижает нагрузку на оперативную память в сотни раз.

Способ 3. Потоковая обработка и вывод отчета

Если задача заключается в программной обработке найденных дублей, избегайте выгрузки всего результата в память через Запрос.Выполнить().Выгрузить(). Это создает огромную нагрузку на оперативную память клиентского или серверного сеанса. Используем потоковую выборку:


Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    // Обработка каждой записи по отдельности
КонецЦикла;

Для экранного отчета наилучшим инструментом является Система компоновки данных (СКД). СКД самостоятельно оптимизирует постраничный вывод, эффективно использует оперативную память и позволяет пользователю гибко настраивать группировки и отборы.

Способ 4. Типовые механизмы конфигураций (БСП)

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

В состав БСП входит обработка «Поиск и удаление дублирующихся элементов» (или «Поиск и замена дублей»). Рассмотрим ее преимущества:

  1. Возможность тонкой настройки правил сравнения: по точному совпадению наименования, по схожим словам (с учетом нечеткого поиска) или по комбинации реквизитов (например, наименование + артикул).
  2. Режим предварительного просмотра найденных дублей перед их объединением.
  3. Автоматическая замена ссылок во всех проведенных документах, регистрах сведений и накопления с помощью механизма ЗаменаСсылок, что защищает информационную базу от появления битых ссылок.
← На главную