При работе с обменами данных в 1С:Конвертация данных 2.1 (КД 2.1) часто возникает задача оптимизации: нужно записывать в базу-приемник только те объекты, которые действительно претерпели изменения в источнике. Это позволяет существенно снизить нагрузку на сервер, избежать лишних записей в журнал регистрации и не провоцировать ненужный запуск подписок на события. В этой статье мы подробно разберем, почему стандартные методы не всегда работают, и проанализируем эффективные программные способы решения этой задачи.
Многие начинающие разработчики пытаются использовать стандартный метод Модифицированность() или проверять ВерсияДанных. Разберем, почему в контексте обмена через формат XML это не приносит желаемого результата.
Проблема метода Модифицированность(): Когда обработка «Универсальный обмен данными в формате XML» считывает данные из файла, она создает программный объект и начинает последовательно присваивать значения его реквизитам. Даже если новое значение реквизита полностью совпадает с текущим значением в базе, платформа 1С при любом присвоении (Объект.Реквизит = Значение) автоматически взводит флаг модифицированности в значение Истина. Таким образом, к моменту выполнения обработчика «ПослеЗагрузки», объект всегда будет считаться измененным.
Проблема свойства ВерсияДанных: Это свойство представляет собой уникальный идентификатор версии записи в базе данных. Проверять его напрямую (ВерсияДанных <> Объект.Ссылка.ВерсияДанных) бессмысленно, так как при выгрузке из базы-источника передается версия данных именно из источника. Она гарантированно будет отличаться от версии того же объекта в базе-приемнике, даже если состав реквизитов идентичен.
Прежде чем приступать к написанию кода, проверим наличие штатных инструментов. В обработке «Универсальный обмен данными XML» на вкладке «Параметры загрузки» (или в настройках самой выгрузки) существует флаг «Записывать только измененные объекты».
При активации этой опции система самостоятельно выполняет сравнение загруженных данных с текущим состоянием объекта в базе. Однако у этого способа есть несколько существенных ограничений:
Если ваша задача выходит за рамки простого «не записывать», перейдем к программным методам.
Проанализируем самый надежный программный метод. Мы будем сравнивать текущий объект, который находится в памяти обработки загрузки, с его «копией», считанной напрямую из базы данных приемника. Оптимальное место для этого — обработчик «ПередЗаписью» в правилах конвертации объекта (ПКО) или событие «ПослеЗагрузки».
Рассмотрим пример кода, который можно разместить в обработчике:
// Получаем текущее состояние объекта из базы данных
КопияОбъекта = Объект.Ссылка.ПолучитьОбъект();
Если КопияОбъекта <> Неопределено Тогда
ОбъектИзменен = Ложь;
// Перечисляем реквизиты, которые критичны для изменений
// Для автоматизации можно использовать метаданные
Для Каждого Реквизит Из Метаданные.Документы.ЗаказПокупателя.Реквизиты Цикл
ИмяРеквизита = Реквизит.Имя;
Если Объект[ИмяРеквизита] <> КопияОбъекта[ИмяРеквизита] Тогда
ОбъектИзменен = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
// Если реквизиты совпали, проверяем табличные части
Если Не ОбъектИзменен Тогда
// Здесь должна быть логика сравнения ТЧ (количество строк, значения в колонках)
КонецЕсли;
// Если изменений не обнаружено — отменяем запись
Если Не ОбъектИзменен Тогда
Отказ = Истина;
КонецЕсли;
КонецЕсли;
Важный нюанс: Метод ПолучитьОбъект() является достаточно ресурсоемким, так как считывает все данные объекта из СУБД. Если объектов десятки тысяч, это может замедлить загрузку.
Чтобы ускорить процесс, вместо получения всего объекта целиком, мы можем использовать Запрос. Это особенно эффективно, если нам нужно отслеживать изменения только в ключевых полях (например, дата, сумма, контрагент).
Выясним, как это реализовать. В обработчике «ПослеЗагрузки» мы выполняем запрос к базе приемника, передавая ссылку загружаемого объекта:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Дата,
| СуммаДокумента,
| Контрагент
|ИЗ
| Документ.ЗаказПокупателя
|ГДЕ
| Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
// Сравниваем только нужные поля
Если Объект.Дата = Выборка.Дата
И Объект.СуммаДокумента = Выборка.СуммаДокумента
И Объект.Контрагент = Выборка.Контрагент Тогда
// Данные не изменились — отменяем запись
Отказ = Истина;
КонецЕсли;
КонецЕсли;
Рассмотрим ситуацию, когда нам нужно не просто пропустить запись, а еще и выполнить хронологическое перепроведение всех измененных документов после завершения всей загрузки. Для этого мы будем использовать глобальный параметр конвертации — таблицу значений или массив.
ПередЗагрузкойДанных создадим список для хранения ссылок на измененные объекты.
Параметры.Вставить("СписокИзмененныхОбъектов", Новый СписокЗначений);
ПередЗаписью выполняем сравнение (как в способах выше). Если объект изменен, добавляем его в наш список.
Если Не Отказ Тогда // Если мы решили, что объект изменен и запись состоится
Параметры.СписокИзмененныхОбъектов.Добавить(Объект.Ссылка, Объект.Дата);
КонецЕсли;
ПослеЗагрузкиДанных (который выполняется один раз в самом конце) перебираем наш список, сортируем его по дате и проводим документы.
// Сортируем список по дате (значение в СпискеЗначений)
Параметры.СписокИзмененныхОбъектов.СортироватьПоЗначению();
Для Каждого ЭлементСписка Из Параметры.СписокИзмененныхОбъектов Цикл
ДокОбъект = ЭлементСписка.Значение.ПолучитьОбъект();
Если ДокОбъект <> Неопределено Тогда
Попытка
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщить("Ошибка проведения: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Мы проанализировали несколько стратегий контроля изменений при загрузке. Выбор конкретного метода зависит от ваших требований к производительности:
1. Стандартная галка в обработке — подходит для простых обменов без сложных алгоритмов в ПКО.
2. Метод ПолучитьОбъект() — самый надежный для полной проверки всех реквизитов и табличных частей, но самый медленный.
3. Запрос к БД — идеален для быстрой проверки по «контрольным точкам» (ключевым реквизитам).
4. Использование ДополнительныеСвойства — позволяет передавать флаг необходимости проведения между разными этапами загрузки объекта.
Помните, что использование Отказ = Истина в обработчиках «ПослеЗагрузки» или «ПередЗаписью» не прерывает загрузку всего файла, а лишь отменяет транзакцию записи текущего объекта, что является абсолютно безопасным и эффективным способом оптимизации обмена данными.