Как объединить два элемента справочника в 1С при огромном количестве ссылок?

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

Объединение дублирующихся элементов справочника — стандартная задача для администратора или разработчика 1С. Однако, когда один из элементов встречается в базе данных сотни тысяч раз (например, более 500 000 ссылок), классические подходы и стандартные обработки БСП («Поиск и удаление дублей») гарантированно вызывают зависание системы, переполнение оперативной памяти сервера или блокировки таблиц СУБД. Поиск ссылок в таких объемах может занимать десятки часов, а сам процесс замены — приводить к аварийному завершению работы службы rphost.

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

Почему стандартные инструменты «зависают» и падают?

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

  1. Глобальный поиск ссылок: Метод платформы НайтиПоСсылкам() выполняет сканирование всех таблиц базы данных, где теоретически может использоваться тип заменяемого справочника. На больших базах данных это приводит к колоссальному числу тяжелых запросов типа Full Table Scan.
  2. Эскалация управляемых блокировок: Пытаясь заменить ссылку в сотнях тысяч документов в одной транзакции, стандартная обработка накладывает блокировки на таблицы. Когда число заблокированных записей превышает определенный лимит (обычно 100 000), СУБД эскалирует блокировку до уровня всей таблицы. В этот момент работа всей компании в базе данных полностью останавливается.
  3. Переполнение памяти и транзакционного лога: Попытка записать сотни тысяч объектов в одной транзакции приводит к стремительному росту файла транзакций SQL (LDF) и лавинообразному потреблению оперативной памяти сервером приложений 1С.

---

Способ 1. Использование специализированных интерактивных инструментов

Если конфигурация позволяет использовать сторонние инструменты, настоятельно рекомендуется отказаться от стандартного поиска дублей БСП в пользу обработки «Поиск и замена ссылок» из состава программного комплекса «Инструменты разработчика» (ИР).

Разберем, почему этот инструмент эффективнее в нашей ситуации:

---

Способ 2. Оптимизированный программный метод (порционная замена)

Если задачу необходимо решить программно, главная цель — обойти стандартные проверки и разбить процесс на небольшие порции (батчи). Рассмотрим по шагам, как написать эффективный скрипт замены.

Для минимизации нагрузки мы применим два важнейших правила:

  1. Установка флага ОбменДанными.Загрузка = Истина. Это отключит проверки при записи, проведение документов, расчеты регистров и тяжелые обработчики событий ПередЗаписью и ПриЗаписи.
  2. Разбиение транзакций на порции (например, по 1000–5000 объектов) с обязательной фиксацией транзакции и небольшой паузой между итерациями для освобождения ресурсов СУБД.

Ниже представлен пример шаблона кода для порционной замены ссылок в документах:


// Определяем заменяемый элемент и правильный элемент
СсылкаДубль = Справочники.Номенклатура.НайтиПоКоду("00001"); // тот, который удаляем
СсылкаОригинал = Справочники.Номенклатура.НайтиПоКоду("00002"); // тот, на который меняем

// Находим все ссылки на дубль
МассивДляПоиска = Новый Массив;
МассивДляПоиска.Добавить(СсылкаДубль);
ТаблицаСсылок = НайтиПоСсылкам(МассивДляПоиска);

// Фильтруем таблицу, оставляя только документы
ТаблицаДокументов = ТаблицаСсылок.Скопировать();
// При необходимости можно настроить отбор по конкретным метаданным

ВсегоСсылок = ТаблицаДокументов.Количество();
НомерСтроки = 0;
РазмерПорции = 1000; // Оптимальный размер порции для одной транзакции

Пока НомерСтроки < ВсегоСсылок Цикл
    
    НачатьТранзакцию();
    
    Для Итератор = 1 По РазмерПорции Цикл
        
        Если НомерСтроки >= ВсегоСсылок Тогда
            Прервать;
        КонецЕсли;
        
        СтрокаСсылки = ТаблицаДокументов[НомерСтроки];
        ОбъектСсылка = СтрокаСсылки.Ссылка;
        
        // Проверяем, что это действительно объект ссылочного типа документа
        Если Метаданные.Документы.Содержит(ОбъектСсылка.Метаданные()) Тогда
            
            Попытка
                ДокументОбъект = ОбъектСсылка.ПолучитьОбъект();
                Если ДокументОбъект <> Неопределено Тогда
                    
                    // Включаем режим загрузки для отключения подписок и проведения
                    ДокументОбъект.ОбменДанными.Загрузка = Истина;
                    
                    // Вызываем процедуру замены во всех реквизитах и табличных частях
                    // (Платформенный метод заполнения или ручной обход реквизитов)
                    ЗаменитьСсылкиВОбъекте(ДокументОбъект, СсылкаДубль, СсылкаОригинал);
                    
                    ДокументОбъект.Записать();
                КонецЕсли;
            Исключение
                // Логируем ошибки блокировок или доступа
                ЗаписьЖурналаРегистрации("ЗаменаДублей.Ошибка", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
            КонецПопытки;
            
        КонецЕсли;
        
        НомерСтроки = НомерСтроки + 1;
        
    КонецЦикла;
    
    ЗафиксироватьТранзакцию();
    
    // Делаем небольшую технологическую паузу для СУБД
    #Если Клиент Тогда
        ОбработкаПрерыванияПользователя();
    #КонецЕсли
    
КонецЦикла;

Для реализации функции ЗаменитьСсылкиВОбъекте можно использовать универсальный обход всех реквизитов шапки и строк табличных частей объекта метаданных через программный доступ к свойствам.

---

Способ 3. Прямая замена через SQL (максимальная скорость)

Прямое обновление данных на уровне СУБД позволяет выполнить замену 500 000 ссылок за несколько секунд или минут. Однако этот метод является недокументированным, нарушает лицензионное соглашение 1С и несет огромные риски повреждения логической целостности базы данных. Применять его можно только на клиент-серверном варианте работы, предварительно сделав полную резервную копию базы данных SQL!

Проанализируем особенности хранения ссылок 1С в таблицах SQL и шаги, необходимые для безопасного выполнения операции:

Шаг 1. Специфика хранения GUID в 1С и SQL

В базах данных SQL Server ссылки 1С хранятся в бинарных полях типа binary(16). При этом платформа 1С записывает уникальный идентификатор (UUID) в инвертированном (перевернутом) виде. Чтобы написать корректный запрос UPDATE, вам необходимо программно перевести строковый GUID 1С в HEX-последовательность Microsoft SQL Server.

Смещение байтов происходит по определенному алгоритму (байты переставляются местами группами: первые три блока UUID переворачиваются задом наперед, последние блоки сохраняют свой порядок).

Шаг 2. Определение физических имен таблиц

Имена таблиц в SQL-базе генерируются платформой динамически (например, _Reference45 для справочников или _Document120_VT1305 для табличных частей). Чтобы узнать, в каких именно таблицах и колонках базы данных SQL используется заменяемый справочник, воспользуйтесь программным методом 1С:

ПолучитьСтруктуруХраненияБазыДанных()

Этот метод вернет точное соответствие имен метаданных 1С и физических таблиц СУБД. На основе этих данных формируется список SQL-запросов на замену.

Шаг 3. Пример SQL-скрипта замены

После получения HEX-представлений старого (дубля) и нового (оригинального) GUID, запрос в SQL Management Studio для конкретной таблицы (например, табличной части документа) будет выглядеть следующим образом:


-- ПРИМЕР ЗАПРОСА В СУБД SQL
-- @OldHex - двоичное представление заменяемого GUID
-- @NewHex - двоичное представление оригинального GUID

UPDATE _Document120_VT1305
SET _Fld1306RRef = 0x80E100155D05070211E69CB2CD0C5BB3 -- Новый HEX
WHERE _Fld1306RRef = 0x80E100155D05070211E69CB2CD0C5BA2 -- Старый HEX

Шаг 4. Борьба с кэшированием — критически важно!

Сервер приложений 1С активно кэширует данные объектов в своей оперативной памяти. Если вы измените значения напрямую в SQL, сервер 1С об этом изменении ничего не узнает. В результате пользователи продолжат видеть старый дубль, а при записи объектов возникнут конфликты блокировок и ошибки несоответствия версий.

Чтобы изменения корректно применились, обязательно выполните следующие действия сразу после SQL-апдейта:

  1. Полностью перезапустите службу сервера 1С (процессы rphost) для очистки кэша сеансов.
  2. Очистите локальный кэш конфигурации на рабочих станциях пользователей.
  3. Запустите стандартную процедуру «Тестирование и исправление» (ТиИ) в конфигураторе 1С с флагами «Реструктуризация таблиц» и «Пересчет итогов» (чтобы СУБД пересчитала итоги регистров накопления, где произошла замена).

Также помните, что если для справочника или документов включена функция «История данных» (версионирование), прямая SQL-замена не обновит исторические XML-версии объектов. Это может привести к ошибкам при попытке просмотреть историю изменений в интерфейсе программы.

---

Резюме и рекомендации

Выбор оптимального метода напрямую зависит от времени, которым вы располагаете, и вашей квалификации:

← На главную