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