Каждый практикующий разработчик и администратор баз данных 1С рано или поздно сталкивается с проблемой дублирования элементов в справочниках. В старых конфигурациях на обычных формах, таких как «Бухгалтерия предприятия 2.0», этот вопрос стоит особенно остро. Когда объем номенклатуры достигает десятков тысяч позиций, а у каждой из них обнаруживается по несколько дубликатов, стандартная обработка «Поиск и удаление дублей» превращается в кошмар. Она может обрабатывать всего по 100 позиций в день, зависать, перегружать сервер и приводить к взаимоблокировкам пользователей.
В этой статье мы подробно разберем, почему стандартные механизмы 1С работают так медленно, проанализируем узкие места системы и пошагово разработаем стратегию создания собственной сверхбыстрой обработки для пакетной замены дубликатов.
Прежде чем приступать к написанию своего кода, давайте разберем физический смысл процессов, которые происходят в системе при замене ссылок стандартными средствами. Это поможет нам понять, на чем именно теряется драгоценное время.
ПередЗаписью, ПриЗаписи, ОбработкаПроведения. Запускаются многочисленные проверки остатков, пересчеты цен, заполнение дополнительных реквизитов и сверка взаиморасчетов, которые совершенно не нужны при простой технической замене одной ссылки на другую.Для того чтобы обойти все ограничения стандартного механизма 1С, нам необходимо спроектировать собственную архитектуру замены ссылок. Мы применим комплексный подход, основанный на пакетной обработке и отключении лишних механизмов платформы.
Вместо того чтобы изменять документы поштучно для каждого найденного дубля, мы должны сначала собрать всю карту заменяемых значений. Нам нужно составить соответствие, где ключом будет выступать дубликат, а значением — правильный оригинальный элемент (ссылка-приемник).
Алгоритм должен работать следующим образом:
НайтиПоСсылкам().Посмотрим на пример кода, реализующего групповую подготовку данных для замены:
// Создаем таблицу соответствия заменяемых элементов
ТаблицаЗамены = Новый ТаблицаЗначений;
ТаблицаЗамены.Колонки.Добавить("Дубль", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаЗамены.Колонки.Добавить("Оригинал", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
// Заполняем таблицу заменяемых элементов (пример заполнения)
// СтрокаЗамены = ТаблицаЗамены.Добавить();
// СтрокаЗамены.Дубль = СсылкаНаДубль;
// СтрокаЗамены.Оригинал = СсылкаНаОригинал;
// Помещаем заменяемые ссылки в массив для поиска мест использования
МассивДублей = ТаблицаЗамены.ВыгрузитьКолонку("Дубль");
ТаблицаМестИспользования = НайтиПоСсылкам(МассивДублей);
// Группируем таблицу мест использования по колонке "Данные" (самому объекту, где сидит ссылка)
ТаблицаМестИспользования.Свернуть("Данные");
Для того чтобы система не тратила время на запуск тяжелых проверок в модулях объектов и подписках на события, мы обязаны использовать специализированный режим записи. Присвоение свойству ОбменДанными.Загрузка значения Истина сообщает платформе 1С, что объект записывается в режиме импорта данных, и выполнять стандартные прикладные проверки не требуется.
Разберем, как применить это на практике:
Для Каждого СтрокаМеста Из ТаблицаМестИспользования Цикл
// Получаем объект для изменения
СсылкаНаОбъект = СтрокаМеста.Данные;
// Обрабатываем только ссылочные типы, поддерживающие изменение объектов
Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(СсылкаНаОбъект)) Тогда
ОбъектМодификации = СсылкаНаОбъект.ПолучитьОбъект();
ИначеЕсли Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(СсылкаНаОбъект)) Тогда
ОбъектМодификации = СсылкаНаОбъект.ПолучитьОбъект();
Иначе
Продолжить; // Пропускаем неподдерживаемые типы метаданных
КонецЕсли;
Если ОбъектМодификации = Неопределено Тогда
Продолжить;
КонецЕсли;
// Включаем режим загрузки для отключения триггеров и проверок
ОбъектМодификации.ОбменДанными.Загрузка = Истина;
// Здесь выполняем замену ссылок в реквизитах и табличных частях объекта...
// Записываем объект напрямую в СУБД без проведения
Если Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(СсылкаНаОбъект)) Тогда
ОбъектМодификации.Записать(РежимЗаписиДокумента.Запись);
Иначе
ОбъектМодификации.Записать();
КонецЕсли;
КонецЦикла;
Если мы не отключим регистрацию изменений, СУБД будет тратить колоссальные ресурсы на запись служебной информации для последующей синхронизации. Чтобы замена происходила «тихо» и максимально быстро, мы заблокируем автозаполнение получателей обмена для каждого изменяемого объекта.
Для этого дополним наш алгоритм записи следующими строками кода:
ОбъектМодификации.ОбменДанными.Загрузка = Истина;
// Отключаем регистрацию на узлах планов обмена
ОбъектМодификации.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
// Если в вашей конфигурации используются механизмы БСП (Библиотеки Стандартных Подсистем),
// может потребоваться отключить регистрацию через дополнительные свойства:
ОбъектМодификации.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов", Истина);
Если вам предстоит заменить миллионы ссылок в старой базе данных, даже отключение проверок через Загрузка = Истина может оказаться недостаточным из-за того, что СУБД продолжает обновлять индексы и итоги таблиц регистров при записи движений документов.
Самый мощный способ ускорения в этом случае — временное программное отключение использования итогов по ключевым регистрам накопления и бухгалтерии перед началом выполнения обработки. После завершения всех замен итоги включаются обратно одним пакетом, и система пересчитывает их за один проход, что в разы эффективнее поштучного пересчета.
Проанализируем пример управления итогами:
// Пример отключения итогов для регистра бухгалтерии "Хозрасчетный"
РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Ложь);
// Выполняем массовую замену дублей во всех документах
// ... Код замены дублей ...
// Включаем итоги обратно и запускаем их полный пересчет
РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Истина);
РегистрыБухгалтерии.Хозрасчетный.ПересчитатьИтоги();
Важно: Данную операцию следует выполнять монопольно, когда в базе данных нет активных пользователей, чтобы избежать системных ошибок и обеспечить максимальную скорость работы СУБД.
В закрытых периодах («прошлых годах») перепроводить документы или изменять сами документы крайне нежелательно, чтобы случайно не нарушить финансовую отчетность. При этом ссылки на дубли в движениях регистров все равно нужно актуализировать.
Решением является прямая перезапись наборов записей регистров бухгалтерии и регистров накопления. Мы считываем набор записей регистра по регистратору (документу), производим замену ссылок внутри набора и записываем его обратно с флагом ОбменДанными.Загрузка = Истина. Сами документы при этом остаются нетронутыми, а проводки и движения обновляются мгновенно.
Если у вас нет свободного времени на написание и тестирование собственного сложного алгоритма с нуля, рекомендуется обратиться к готовым наработкам профессионального сообщества 1С (например, на портале Инфостарт). Практически все опытные внедренцы сталкивались с этой задачей и выкладывали оптимизированные инструменты.
При поиске готовых решений ориентируйтесь на следующие ключевые фразы:
Резюмируя все вышесказанное: не пытайтесь использовать типовую обработку удаления дублей «в лоб» на больших объемах данных. Объединяйте замены в пакеты, используйте свойства отключения проверок, временно гасите расчет итогов — и тогда задача, которая раньше решалась неделями, будет выполнена за считанные минуты!