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

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

Каждый практикующий разработчик и администратор баз данных 1С рано или поздно сталкивается с проблемой дублирования элементов в справочниках. В старых конфигурациях на обычных формах, таких как «Бухгалтерия предприятия 2.0», этот вопрос стоит особенно остро. Когда объем номенклатуры достигает десятков тысяч позиций, а у каждой из них обнаруживается по несколько дубликатов, стандартная обработка «Поиск и удаление дублей» превращается в кошмар. Она может обрабатывать всего по 100 позиций в день, зависать, перегружать сервер и приводить к взаимоблокировкам пользователей.

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

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

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

  1. Поштучная циклическая перезапись объектов. Это главный архитектурный недостаток стандартной обработки. Представьте, что у вас есть элемент «Товар А» и его дубликаты «Товар А_дубль1», «Товар А_дубль2». Все эти три элемента используются в одном и том же документе «Реализация товаров и услуг». Стандартный механизм найдет этот документ и последовательно перезапишет его три раза! Сначала заменит первый дубль на оригинал и запишет документ. Затем считает этот же документ снова, заменит второй дубль и опять запишет. Мы получаем тройную (а при большом количестве дублей — многократную) избыточную нагрузку на чтение и запись одной и той же транзакции.
  2. Автоматический пересчет итогов регистров. При стандартной перезаписи или перепроведении документов система 1С инициирует автоматический пересчет итогов в регистрах накопления и бухгалтерии. Если база данных ведется много лет и имеет внушительный объем, пересчет итогов при записи каждого отдельного документа занимает до 90% всего процессорного времени СУБД.
  3. Выполнение тяжелой бизнес-логики. При обычной перезаписи объектов конфигурации срабатывают встроенные подписки на события: ПередЗаписью, ПриЗаписи, ОбработкаПроведения. Запускаются многочисленные проверки остатков, пересчеты цен, заполнение дополнительных реквизитов и сверка взаиморасчетов, которые совершенно не нужны при простой технической замене одной ссылки на другую.
  4. Регистрация изменений для планов обмена. Если ваша база данных участвует в распределенной информационной базе (РИБ) или синхронизируется с другими конфигурациями (например, «Управление торговлей»), то изменение каждого документа регистрируется на узлах планов обмена. Миллионы лишних записей в таблицах регистрации изменений мгновенно перегружают СУБД.
  5. Блокировки таблиц. При длительной поштучной обработке документов транзакции СУБД остаются открытыми слишком долго, блокируя смежные таблицы и парализуя работу пользователей в многопользовательском режиме.

Разбираем решение по шагам: как ускорить замену в десятки раз

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

Шаг 1. Реализация пакетной замены ссылок

Вместо того чтобы изменять документы поштучно для каждого найденного дубля, мы должны сначала собрать всю карту заменяемых значений. Нам нужно составить соответствие, где ключом будет выступать дубликат, а значением — правильный оригинальный элемент (ссылка-приемник).

Алгоритм должен работать следующим образом:

  1. Программно находим все места использования заменяемых дублей с помощью метода НайтиПоСсылкам().
  2. Полученную таблицу связей группируем по изменяемым объектам (документам, справочникам, регистрам сведения).
  3. Каждый изменяемый объект считываем из базы данных ровно один раз.
  4. Внутри объекта в памяти (в его реквизитах и табличных частях) производим замену всех найденных дублей на оригиналы.
  5. Записываем объект в базу данных один-единственный раз.

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


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

// Заполняем таблицу заменяемых элементов (пример заполнения)
// СтрокаЗамены = ТаблицаЗамены.Добавить();
// СтрокаЗамены.Дубль = СсылкаНаДубль;
// СтрокаЗамены.Оригинал = СсылкаНаОригинал;

// Помещаем заменяемые ссылки в массив для поиска мест использования
МассивДублей = ТаблицаЗамены.ВыгрузитьКолонку("Дубль");
ТаблицаМестИспользования = НайтиПоСсылкам(МассивДублей);

// Группируем таблицу мест использования по колонке "Данные" (самому объекту, где сидит ссылка)
ТаблицаМестИспользования.Свернуть("Данные");

Шаг 2. Отключение бизнес-логики через ОбменДанными.Загрузка

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

Разберем, как применить это на практике:


Для Каждого СтрокаМеста Из ТаблицаМестИспользования Цикл
    
    // Получаем объект для изменения
    СсылкаНаОбъект = СтрокаМеста.Данные;
    
    // Обрабатываем только ссылочные типы, поддерживающие изменение объектов
    Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(СсылкаНаОбъект)) Тогда
        ОбъектМодификации = СсылкаНаОбъект.ПолучитьОбъект();
    ИначеЕсли Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(СсылкаНаОбъект)) Тогда
        ОбъектМодификации = СсылкаНаОбъект.ПолучитьОбъект();
    Иначе
        Продолжить; // Пропускаем неподдерживаемые типы метаданных
    КонецЕсли;
    
    Если ОбъектМодификации = Неопределено Тогда
        Продолжить;
    КонецЕсли;

    // Включаем режим загрузки для отключения триггеров и проверок
    ОбъектМодификации.ОбменДанными.Загрузка = Истина;
    
    // Здесь выполняем замену ссылок в реквизитах и табличных частях объекта...
    
    // Записываем объект напрямую в СУБД без проведения
    Если Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(СсылкаНаОбъект)) Тогда
        ОбъектМодификации.Записать(РежимЗаписиДокумента.Запись);
    Иначе
        ОбъектМодификации.Записать();
    КонецЕсли;
    
КонецЦикла;

Шаг 3. Исключение регистрации изменений в планах обмена

Если мы не отключим регистрацию изменений, СУБД будет тратить колоссальные ресурсы на запись служебной информации для последующей синхронизации. Чтобы замена происходила «тихо» и максимально быстро, мы заблокируем автозаполнение получателей обмена для каждого изменяемого объекта.

Для этого дополним наш алгоритм записи следующими строками кода:


ОбъектМодификации.ОбменДанными.Загрузка = Истина;
// Отключаем регистрацию на узлах планов обмена
ОбъектМодификации.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;

// Если в вашей конфигурации используются механизмы БСП (Библиотеки Стандартных Подсистем),
// может потребоваться отключить регистрацию через дополнительные свойства:
ОбъектМодификации.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов", Истина);

Шаг 4. Временное отключение итогов регистров

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

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

Проанализируем пример управления итогами:


// Пример отключения итогов для регистра бухгалтерии "Хозрасчетный"
РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Ложь);

// Выполняем массовую замену дублей во всех документах
// ... Код замены дублей ...

// Включаем итоги обратно и запускаем их полный пересчет
РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Истина);
РегистрыБухгалтерии.Хозрасчетный.ПересчитатьИтоги();

Важно: Данную операцию следует выполнять монопольно, когда в базе данных нет активных пользователей, чтобы избежать системных ошибок и обеспечить максимальную скорость работы СУБД.

Шаг 5. Прямая замена в движениях регистров без перепроведения документов

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

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

Использование готовых решений из сообщества

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

При поиске готовых решений ориентируйтесь на следующие ключевые фразы:

Резюмируя все вышесказанное: не пытайтесь использовать типовую обработку удаления дублей «в лоб» на больших объемах данных. Объединяйте замены в пакеты, используйте свойства отключения проверок, временно гасите расчет итогов — и тогда задача, которая раньше решалась неделями, будет выполнена за считанные минуты!

← На главную