Как эффективно найти дубликаты в справочнике 1С по реквизиту?

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

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

Метод 1. Анализ и исправление вложенных циклов

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


// Ошибочный вариант
Пока Счетчик < ТЗ.Количество() Цикл
    СчетчикВнутр = 0;
    Пока СчетчикВнутр < ТЗ.Количество() Цикл
        Если ТЗ[Счетчик].ЗаводскойНомер = ТЗ[СчетчикВнутр].ЗаводскойНомер Тогда
            // Проблема: здесь элемент сравнивается сам с собой
            НоваяСтрока = Объект.УдвоенныеПриборыУчета.Добавить();
            // Заполнение данных...
        КонецЕсли;
        СчетчикВнутр = СчетчикВнутр + 1;
    КонецЦикла;
    Счетчик = Счетчик + 1;
КонецЦикла;

Главная проблема этого кода заключается в том, что условие ТЗ[Счетчик].ЗаводскойНомер = ТЗ[СчетчикВнутр].ЗаводскойНомер всегда будет истинным, когда индексы совпадут. В результате в список дублей попадут абсолютно все элементы справочника. Чтобы исправить эту ситуацию, необходимо добавить проверку на несовпадение индексов:


Если Счетчик <> СчетчикВнутр И ТЗ[Счетчик].ЗаводскойНомер = ТЗ[СчетчикВнутр].ЗаводскойНомер Тогда
    // Теперь мы нашли реальный дубль
КонецЕсли;

Важное замечание: Алгоритм с вложенными циклами имеет сложность O(n²). Для 46 000 элементов системе придется выполнить более 2 миллиардов операций сравнения. Это крайне неэффективно и может привести к зависанию сеанса.

Метод 2. Сортировка таблицы значений (Линейный поиск)

Разберем гораздо более быстрый алгоритм, который требует всего одного прохода по данным. Идея заключается в предварительной сортировке данных. Если отсортировать таблицу по полю ЗаводскойНомер, то все дубликаты окажутся в списке друг за другом.

Проанализируем шаги реализации:

  1. Выгружаем данные в ТаблицаЗначений.
  2. Применяем метод ТЗ.Сортировать("ЗаводскойНомер");.
  3. Проходим циклом от 1 до конца таблицы, сравнивая текущий элемент с предыдущим.

ТЗ.Сортировать("ЗаводскойНомер");
Для Счетчик = 1 По ТЗ.Количество() - 1 Цикл
    Если ТЗ[Счетчик].ЗаводскойНомер = ТЗ[Счетчик - 1].ЗаводскойНомер Тогда
        // Найден дубль!
        // Нужно дополнительно проверять, не выводили ли мы этот номер ранее
        // если дублей больше двух подряд.
    КонецЕсли;
КонецЦикла;

Этот метод работает значительно быстрее, так как основное время тратится на сортировку (O(n log n)), а последующий обход выполняется за один цикл.

Метод 3. Поиск дублей с помощью запроса (Рекомендуемый способ)

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

Шаг 1. Поиск дублирующихся номеров. Нам нужно найти те заводские номера, которые встречаются более одного раза.


ВЫБРАТЬ
    ЗаводскойНомер
ПОМЕСТИТЬ ВТ_ДубликатыНомеров
ИЗ

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

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


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

Такой запрос мгновенно вернет все группы дублей. Мы можем использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ вместо условия В (ВЫБРАТЬ...) для еще большей оптимизации на некоторых СУБД.

Метод 4. Оптимизация через объект «Соответствие»

Если нам необходимо найти дубликаты программно при обработке внешних данных (например, загрузка из Excel), рассмотрим использование объекта Соответствие. Поиск по ключу в нем работает практически мгновенно.


КэшНомеров = Новый Соответствие;
Для Каждого СтрокаДанных Из ВходящиеДанные Цикл
    Если КэшНомеров[СтрокаДанных.ЗаводскойНомер] <> Неопределено Тогда
        // Мы уже встречали этот номер - это дубль!
        Сообщить("Найден дубль: " + СтрокаДанных.ЗаводскойНомер);
    Иначе
        КэшНомеров.Вставить(СтрокаДанных.ЗаводскойНомер, Истина);
    КонецЕсли;
КонецЦикла;

Профессиональные нюансы и частые ошибки

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

  1. Пустые значения: Если у части приборов заводской номер не заполнен, система посчитает их все дубликатами друг друга. Всегда добавляйте условие ЗаводскойНомер <> "" в запросах или коде.
  2. Пробелы в строках: "12345" и "12345 " — это разные строки для системы, но фактически это дубли. Рекомендуем использовать функцию СокрЛП() перед сравнением или очищать данные при записи.
  3. Индексация: Чтобы запрос по реквизиту ЗаводскойНомер работал быстро на 46 000+ записях, убедитесь, что в метаданных справочника для этого реквизита установлено свойство Индексировать.
  4. Механизм БСП: В современных конфигурациях лучше использовать встроенную обработку "Поиск и удаление дублей". Она не просто помечает объекты на удаление, но и выполняет "слияние" — заменяет ссылки на дубли во всех документах базы на один выбранный "эталонный" элемент. Это критически важно для сохранения целостности учета.

Подводя итог, можно сказать: для быстрого поиска внутри базы всегда используйте Запросы. Если данные находятся в оперативной памяти (в таблице значений) — используйте Сортировку или Соответствие. Никогда не используйте вложенные циклы для больших объемов данных.

← На главную