Многократная запись регистров сведений и накопления
#std792
Область применения: управляемое приложение, мобильное приложение, обычное приложение.
1. Не рекомендуется записывать наборы записей регистров сведений и накоплений в цикле по одной или нескольким записям. Это в несколько раз медленнее по сравнению с записью регистра одним набором. В большинстве случаев следует ориентироваться на порционную запись наборов из 1000 записей.
2. Рекомендуется избегать перезаписи большого набора, в котором меняется не более 30% записей. Вместо этого следует записывать только изменения.
Перезапись набора приводит к удалению всех записей по отбору набора записей и вставке всех новых записей набора, включая те, которые не менялись. Это в несколько раз медленнее по сравнению с добавлением, обновлением и удалением только измененных записей, если измененных записей не более 30% от всех записей в наборе. Помимо этого, избыточно растет журнал транзакций СУБД (пропорционально количеству удалений и вставки записей).
3. Для оптимальной записи регистров следует применять режимы замещения в сочетании с вычислением измененных записей.
В частном случае, когда нужно только добавить записи в пустой регистр, достаточно в методе Записать набора записей задать параметру РежимЗамещения значение Ложь (добавление). В остальных случаях, рекомендуется использовать режимы замещения Слияние, Удаление (начиная с версии платформы 1С:Предприятие 8.3.26) и Обновление (начиная с версии платформы 1С:Предприятие 8.3.27). Они позволяют обрабатывать записи пакетно, порциями.
4. Примеры.
4.1. Состав добавляемых, обновляемых и удаляемых записей известен и требуется применить эти изменения к базе данных.
Неправильно записывать наборы записей в цикле:
Блокировка = Новый БлокировкаДанных();
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПользовательскиеНастройки");
ЭлементБлокировки.УстановитьЗначение("Пользователь", ПользовательСсылка);
НачатьТранзакцию();
Попытка
Блокировка.Заблокировать();
Для Каждого ЗначениеНастройки Из Настройки Цикл
УдаляемыеЗаписи = РегистрыСведений.ПользовательскиеНастройки.СоздатьНаборЗаписей();
УдаляемыеЗаписи.Отбор.ИдентификаторКоманды.Установить(ЗначениеНастройки.ИдентификаторКоманды);
УдаляемыеЗаписи.Отбор.Пользователь.Установить(ПользовательСсылка);
УдаляемыеЗаписи.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки
Правильно записать изменения в регистр одним набором записей:
Блокировка = Новый БлокировкаДанных();
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПользовательскиеНастройки");
ЭлементБлокировки.УстановитьЗначение("Пользователь", ПользовательСсылка);
НачатьТранзакцию();
Попытка
Блокировка.Заблокировать();
УдаляемыеЗаписи = РегистрыСведений.ПользовательскиеНастройки.СоздатьНаборЗаписей();
Для Каждого ЗначениеНастройки Из Настройки Цикл
УдаляемаяЗапись = УдаляемыеЗаписи.Добавить();
УдаляемаяЗапись.ИдентификаторКоманды = ЗначениеНастройки.ИдентификаторКоманды;
УдаляемаяЗапись.Пользователь = ПользовательСсылка;
КонецЦикла;
УдаляемыеЗаписи.Записать(РежимЗамещения.Удаление);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки
4.2. Заполнение значения нового реквизита (или ресурса) регистра в отложенном обработчике обновления. Обрабатывается порция данных, зарегистрированных для обновления: если значение еще не заполнено, тогда заполнить, иначе не менять.
Неправильно читать и записывать наборы записей в цикле:
Процедура ОбработатьДанныеДляПереходаНаНовуюВерсию(Параметры) Экспорт
ПорцияЗначенийИзмерений = ОбновлениеИнформационнойБазы.ДанныеДляОбновленияВМногопоточномОбработчике(Параметры);
ПолноеИмяРегистра = Метаданные.РегистрыСведений.ДанныеБизнесПроцессов.ПолноеИмя();
Для Каждого Данные Из ПорцияЗначенийИзмерений Цикл
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяРегистра);
ЭлементБлокировки.УстановитьЗначение("Владелец", Данные.Владелец);
НачатьТранзакцию();
Попытка
Блокировка.Заблокировать();
НаборЗаписей = РегистрыСведений.ДанныеБизнесПроцессов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Владелец.Установить(Данные.Владелец);
НаборЗаписей.Прочитать();
// Установка новых значений в набор записей.
...
ОбновлениеИнформационнойБазы.ЗаписатьНаборЗаписей(НаборЗаписей);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
...
КонецПопытки;
КонецЦикла;
...
КонецПроцедуры
Правильно рассчитать изменения одним запросом и записать изменения в регистр одним набором записей:
Процедура ОбработатьДанныеДляПереходаНаНовуюВерсию(Параметры) Экспорт
ПорцияЗначенийИзмерений = ОбновлениеИнформационнойБазы.ДанныеДляОбновленияВМногопоточномОбработчике(Параметры);
ПолноеИмяРегистра = Метаданные.РегистрыСведений.ДанныеБизнесПроцессов.ПолноеИмя();
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяРегистра);
ЭлементБлокировки.ИсточникДанных = ПорцияЗначенийИзмерений;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Владелец", "Владелец");
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ПорцияЗначенийИзмерений", ПорцияЗначенийИзмерений);
Запрос.Текст =
"ВЫБРАТЬ
| *,
| ЗНАЧЕНИЕ(Перечисление.СостоянияБизнесПроцессов.Активен) КАК Состояние
|ИЗ
| РегистрСведений.ДанныеБизнесПроцессов КАК ДанныеБизнесПроцессов
|ГДЕ
| ДанныеБизнесПроцессов.Состояние = ЗНАЧЕНИЕ(Перечисление.СостоянияБизнесПроцессов.ПустаяСсылка)
| И (ДанныеБизнесПроцессов.Владелец) В (&ПорцияЗначенийИзмерений)";
НачатьТранзакцию();
Попытка
БлокировкаДанных.Заблокировать();
Выгрузка = Запрос.Выполнить().Выгрузить();
НаборЗаписей = РегистрыСведений.ДанныеБизнесПроцессов.СоздатьНаборЗаписей();
НаборЗаписей.Загрузить(Выгрузка);
ОбновлениеИнформационнойБазы.ЗаписатьНаборЗаписей(НаборЗаписей, РежимЗамещения.Обновление);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
...
КонецПопытки;
...
КонецПроцедуры
4.3. Обновление части записей в независимом регистре сведений. При записи карточки объекта данные меняются частично.
Объект уже записан в базу данных (транзакция может быть еще не зафиксирована), поэтому применяем запрос, чтобы:
- вычислить новые записи без загрузки их в сервер 1С:Предприятия из СУБД;
- сравнить новые записи с существующими записями в обновляемом регистре сведений;
- выбрать только различия:
- лишние записи - для удаления;
- недостающие новые записи и новую версию измененных записей - для слияния.
Например, неправильно:
Процедура ОбновитьСоставыИерархическихГруппПользователей(ГруппыПользователей) Экспорт
ТекстЗапроса =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ГруппыДляОбновления.Родитель КАК ГруппаПользователей,
| ГруппыПользователейСостав.Пользователь КАК Пользователь,
| НЕ ГруппыДляОбновления.Родитель.ПометкаУдаления
| И НЕ ГруппыПользователейСостав.Пользователь.ПометкаУдаления
| И НЕ ГруппыПользователейСостав.Пользователь.Недействителен КАК Используется
|ИЗ
| РегистрСведений.ИерархияГруппПользователей КАК ГруппыДляОбновления
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИерархияГруппПользователей КАК НижестоящиеГруппы
| ПО (НижестоящиеГруппы.Родитель = ГруппыДляОбновления.Родитель)
| И (ГруппыДляОбновления.ГруппаПользователей В (&ГруппыПользователей))
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыПользователей.Состав КАК ГруппыПользователейСостав
| ПО (ГруппыПользователейСостав.Ссылка = НижестоящиеГруппы.ГруппаПользователей)";
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("ГруппыПользователей", ГруппыПользователей);
Блокировка = Новый БлокировкаДанных;
Для Каждого ГруппаПользователей Из ГруппыПользователей Цикл
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СоставыГруппПользователей");
ЭлементБлокировки.УстановитьЗначение("ГруппаПользователей", ГруппаПользователей);
КонецЦикла;
НачатьТранзакцию();
Попытка
Блокировка.Заблокировать();
Выгрузка = Запрос.Выполнить().Выгрузить();
Для Каждого ГруппаПользователей Из ГруппыПользователей Цикл
НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ГруппаПользователей.Установить(ГруппаПользователей);
Отбор = Новый Структура("ГруппаПользователей", ГруппаПользователей);
НайденныеСтроки = Выгрузка.НайтиСтроки(Отбор);
НаборЗаписей.Загрузить(Выгрузка.Скопировать(НайденныеСтроки));
НаборЗаписей.Записать(); // Замена всех записей группы на новые.
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Правильно:
Процедура ОбновитьСоставыИерархическихГруппПользователей(ГруппыПользователей) Экспорт
ТекстЗапроса =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| СоставыГруппПользователей.ГруппаПользователей КАК ГруппаПользователей,
| СоставыГруппПользователей.Пользователь КАК Пользователь
|ИЗ
| РегистрСведений.ИерархияГруппПользователей КАК ГруппыДляОбновления1
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
| ПО (СоставыГруппПользователей.ГруппаПользователей = ГруппыДляОбновления1.Родитель)
| И (ГруппыДляОбновления1.ГруппаПользователей В (&ГруппыПользователей))
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИерархияГруппПользователей КАК ГруппыДляОбновления2
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИерархияГруппПользователей КАК НижестоящиеГруппы
| ПО (НижестоящиеГруппы.Родитель = ГруппыДляОбновления2.Родитель)
| И (ГруппыДляОбновления2.ГруппаПользователей В (&ГруппыПользователей))
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыПользователей.Состав КАК ГруппыПользователейСостав
| ПО (ГруппыПользователейСостав.Ссылка = НижестоящиеГруппы.ГруппаПользователей)
| ПО (ГруппыДляОбновления2.Родитель = СоставыГруппПользователей.ГруппаПользователей)
| И (ГруппыПользователейСостав.Пользователь = СоставыГруппПользователей.Пользователь)
|ГДЕ
| ГруппыПользователейСостав.Пользователь ЕСТЬ NULL
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗЛИЧНЫЕ
| ГруппыДляОбновления.Родитель КАК ГруппаПользователей,
| ГруппыПользователейСостав.Пользователь КАК Пользователь,
| НЕ ГруппыДляОбновления.Родитель.ПометкаУдаления
| И НЕ ГруппыПользователейСостав.Пользователь.ПометкаУдаления
| И НЕ ГруппыПользователейСостав.Пользователь.Недействителен КАК Используется
|ИЗ
| РегистрСведений.ИерархияГруппПользователей КАК ГруппыДляОбновления
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИерархияГруппПользователей КАК НижестоящиеГруппы
| ПО (НижестоящиеГруппы.Родитель = ГруппыДляОбновления.Родитель)
| И (ГруппыДляОбновления.ГруппаПользователей В (&ГруппыПользователей))
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыПользователей.Состав КАК ГруппыПользователейСостав
| ПО (ГруппыПользователейСостав.Ссылка = НижестоящиеГруппы.ГруппаПользователей)
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
| ПО (СоставыГруппПользователей.ГруппаПользователей = ГруппыДляОбновления.Родитель)
| И (СоставыГруппПользователей.Пользователь = ГруппыПользователейСостав.Пользователь)
|ГДЕ
| (СоставыГруппПользователей.Пользователь ЕСТЬ NULL
| ИЛИ ЕСТЬNULL(СоставыГруппПользователей.Используется, ЛОЖЬ) <> (НЕ ГруппыДляОбновления.Родитель.ПометкаУдаления
| И НЕ ГруппыПользователейСостав.Пользователь.ПометкаУдаления
| И НЕ ГруппыПользователейСостав.Пользователь.Недействителен))";
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("ГруппыПользователей", ГруппыПользователей);
РезультатыЗапроса = Запрос.ВыполнитьПакет();
Если РезультатыЗапроса[0].Пустой() И РезультатыЗапроса[1].Пустой() Тогда
Возврат;
КонецЕсли;
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СоставыГруппПользователей");
ЭлементБлокировки.ИсточникДанных = РезультатыЗапроса[0];
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("ГруппаПользователей", "ГруппаПользователей");
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Пользователь", "Пользователь");
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СоставыГруппПользователей");
ЭлементБлокировки.ИсточникДанных = РезультатыЗапроса[1];
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("ГруппаПользователей", "ГруппаПользователей");
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Пользователь", "Пользователь");
НачатьТранзакцию();
Попытка
Блокировка.Заблокировать();
РезультатыЗапроса = Запрос.ВыполнитьПакет();
Если Не РезультатыЗапроса[0].Пустой() Тогда
НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
НаборЗаписей.Загрузить(РезультатыЗапроса[0].Выгрузить());
НаборЗаписей.Записать(РежимЗамещения.Удаление);
КонецЕсли;
Если Не РезультатыЗапроса[1].Пустой() Тогда
НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
НаборЗаписей.Загрузить(РезультатыЗапроса[1].Выгрузить());
НаборЗаписей.Записать(РежимЗамещения.Слияние);
КонецЕсли;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
4.4. Обновление части записей в регистрах, подчиненных регистратору. При перепроведении данные меняются частично.
Похоже на обновление записей в независимых регистрах сведениях, но есть существенные отличия:
- В отличие от независимых регистров сведений, наличие регистратора делает ключом строки строго два стандартных поля Регистратор и НомерСтроки, а также исключает режим замещения Слияние. Кроме того, в режиме замещения Удаление происходит автоматическое обновление поля НомерСтроки у оставшихся записей в пределах каждого регистратора.
- Поэтому для оптимального обновления поле НомерСтроки должно рассматриваться исключительно, как счетчик строк в пределах регистратора, при этом порядок строк не должен иметь значения для прикладного использования.
- При вычислении изменений нужно исключать НомерСтроки из сравнения.
- Сначала сделать набор записей (со сброшенным отбором) для записи в режиме замещения Обновление из добавляемых и обновляемых записей в количестве не более количества удаляемых записей с номерами строк удаляемых записей.
- Если после этого остались записи для удаления, тогда сделать набор записей (со сброшенным отбором) для записи в режиме замещения Удаление из ключей строк оставшихся удаляемых записей.
- В противном случае, сделать набор записей (со сброшенным отбором) для записи в режиме замещения Добавление (или Ложь) из оставшихся добавляемых и обновляемых записей (без включения РасширенныеРежимыЗамещения и заполнения поля НомерСтроки – доступно, начиная с версии 1С:Предприятия 8.3.26).
Рекомендуется оптимизировать запись движений, кроме особых случаев, когда требуется обновление регистра сразу по нескольким регистраторам (закрытие месяца, восстановление последовательности взаиморасчетов и др.).
В тех случаях, когда запись движений выполняется долго, например, табличная часть может быть очень большой (несколько тысяч строк), а изменение табличной части небольшое и затраты на формирование движений и их запись средствами платформы оказываются неудовлетворительными, можно оптимизировать обновление движений по принципу, аналогичному примеру 4.3:
Обычное решение:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения._ДемоОстаткиТоваровВМестахХранения.Записывать = Истина;
Для Каждого СтрокаТовары Из Товары Цикл
Движение = Движения._ДемоОстаткиТоваровВМестахХранения.Добавить();
Движение.Период = Дата;
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Организация = Организация;
Движение.МестоХранения = МестоХранения;
Движение.Номенклатура = СтрокаТовары.Номенклатура;
Движение.Количество = СтрокаТовары.Количество;
КонецЦикла;
КонецПроцедуры
Оптимальное решение:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Документ", Ссылка);
Запрос.Текст =
"ВЫБРАТЬ
| ТекущийРегистр.Регистратор КАК Регистратор,
| ТекущийРегистр.НомерСтроки КАК НомерСтроки
|ИЗ
| РегистрНакопления._ДемоОстаткиТоваровВМестахХранения КАК ТекущийРегистр
| ЛЕВОЕ СОЕДИНЕНИЕ Документ._ДемоОприходованиеТоваров КАК ТекущийДокумент
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ._ДемоОприходованиеТоваров.Товары КАК ТекущийДокументТовары
| ПО (ТекущийДокумент.Ссылка = &Документ)
| И (ТекущийДокументТовары.Ссылка = ТекущийДокумент.Ссылка)
| ПО (ТекущийРегистр.Активность)
| И (ТекущийРегистр.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход))
| И (ТекущийДокумент.Дата = ТекущийРегистр.Период)
| И (ТекущийДокумент.Организация = ТекущийРегистр.Организация)
| И (ТекущийДокумент.МестоХранения = ТекущийРегистр.МестоХранения)
| И (ТекущийДокументТовары.Номенклатура = ТекущийРегистр.Номенклатура)
| И (ТекущийДокументТовары.Количество = ТекущийРегистр.Количество)
|ГДЕ
| ТекущийРегистр.Регистратор = &Документ
| И ТекущийДокумент.Ссылка ЕСТЬ NULL
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ТекущийДокумент.Ссылка КАК Регистратор,
| ТекущийДокумент.Дата КАК Период,
| ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход) КАК ВидДвижения,
| ТекущийДокумент.Организация КАК Организация,
| ТекущийДокумент.МестоХранения КАК МестоХранения,
| ТекущийДокументТовары.Номенклатура КАК Номенклатура,
| ТекущийДокументТовары.Количество КАК Количество
|ИЗ
| Документ._ДемоОприходованиеТоваров КАК ТекущийДокумент
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ._ДемоОприходованиеТоваров.Товары КАК ТекущийДокументТовары
| ПО (ТекущийДокумент.Ссылка = &Документ)
| И (ТекущийДокументТовары.Ссылка = ТекущийДокумент.Ссылка)
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления._ДемоОстаткиТоваровВМестахХранения КАК ТекущийРегистр
| ПО (ТекущийРегистр.Активность)
| И (ТекущийРегистр.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход))
| И (ТекущийРегистр.Период = ТекущийДокумент.Дата)
| И (ТекущийРегистр.Организация = ТекущийДокумент.Организация)
| И (ТекущийРегистр.МестоХранения = ТекущийДокумент.МестоХранения)
| И (ТекущийРегистр.Номенклатура = ТекущийДокументТовары.Номенклатура)
| И (ТекущийРегистр.Количество = ТекущийДокументТовары.Количество)
|ГДЕ
| ТекущийРегистр.ВидДвижения ЕСТЬ NULL";
РезультатыЗапроса = Запрос.ВыполнитьПакет();
КлючиЛишнихЗаписей = РезультатыЗапроса[0].Выгрузить();
НедостающиеЗаписи = РезультатыЗапроса[1].Выгрузить();
КоличествоЛишнихЗаписей = КлючиЛишнихЗаписей.Количество();
КоличествоНедостающихЗаписей = НедостающиеЗаписи.Количество();
Если КоличествоЛишнихЗаписей > КоличествоНедостающихЗаписей Тогда
КоличествоОбновляемыхЗаписей = КоличествоНедостающихЗаписей;
Иначе
КоличествоОбновляемыхЗаписей = КоличествоЛишнихЗаписей;
КонецЕсли;
Если КоличествоОбновляемыхЗаписей > 0 Тогда
НаборЗаписей = РегистрыНакопления._ДемоОстаткиТоваровВМестахХранения.СоздатьНаборЗаписей();
НаборЗаписей.РасширенныеРежимыЗамещения = Истина;
НаборЗаписей.Отбор.Сбросить();
Для Индекс = 0 По КоличествоОбновляемыхЗаписей - 1 Цикл
НоваяЗапись = НаборЗаписей.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, НедостающиеЗаписи[Индекс]);
НоваяЗапись.НомерСтроки = КлючиЛишнихЗаписей[Индекс].НомерСтроки;
КонецЦикла;
НаборЗаписей.Записать(РежимЗамещения.Обновление);
КонецЕсли;
Если КоличествоЛишнихЗаписей > КоличествоНедостающихЗаписей Тогда
НаборЗаписей = РегистрыНакопления._ДемоОстаткиТоваровВМестахХранения.СоздатьНаборЗаписей();
НаборЗаписей.РасширенныеРежимыЗамещения = Истина;
НаборЗаписей.Отбор.Сбросить();
Для Индекс = КоличествоОбновляемыхЗаписей По КоличествоЛишнихЗаписей - 1 Цикл
ЗаполнитьЗначенияСвойств(НаборЗаписей.Добавить(), КлючиЛишнихЗаписей[Индекс]);
КонецЦикла;
НаборЗаписей.Записать(РежимЗамещения.Удаление);
ИначеЕсли КоличествоЛишнихЗаписей < КоличествоНедостающихЗаписей Тогда
НаборЗаписей = РегистрыНакопления._ДемоОстаткиТоваровВМестахХранения.СоздатьНаборЗаписей();
НаборЗаписей.РасширенныеРежимыЗамещения = Истина;
НаборЗаписей.Отбор.Сбросить();
Для Индекс = КоличествоОбновляемыхЗаписей По КоличествоНедостающихЗаписей - 1 Цикл
ЗаполнитьЗначенияСвойств(НаборЗаписей.Добавить(), НедостающиеЗаписи[Индекс]);
КонецЦикла;
НаборЗаписей.Записать(РежимЗамещения.Добавление);
КонецЕсли;
КонецПроцедуры