Массовое изменение записей регистров сведений и накопления (слияние, обновление, удаление)

27.10.25

Разработка - Механизмы платформы 1С

Рассматривается применимость новых режимов замещения наборов регистра сведений и накопления при записи для массовой обработки данных.

Введение

 

С 26 версии платформы появились методы для массового изменения записей регистров сведений и накоплений, данные методы особенно востребованы в системах, где большие объемы данных, и их изменение может занимать длительное время. При использовании "старого функционала" замещения записей набора, где сначала данные удаляются, а затем записываются новые наборы по отбору измерений или регистратору, время массовой обработки данных достаточно велико, что очень негативно сказывает на общей производительности системы и увеличивает технологическое окно обслуживания. С появлением новых вариантов обработки большого объема наборов записей возможно значительно сократить время обработки. Далее рассматриваются особенности новых методов на платформе 8.3.27.1688 с использованием СУБД PostgreSQL 14.17 на тестовом примере. На ИТС описание работы новых методов здесь.

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

 

рис 1. Исходная структура метаданных

 

Продажи идут активно и за месяц внесено 100 000 документов. Бизнес развивается и теперь появилось новое требование, менеджерам необходимо анализировать не только стоимость продажи, но и сумму НДС, который включен в продажу. Задачу можно решать по-разному, один из вариантов состоит в том, чтобы добавить в регистр накопления Продажи новый ресурс Сумма НДС. Получим следующую структуру:

 

рис 2. Новая структура метаданных

 

Теперь необходимо обновить данные в информационной базе. С документами у нас только один способ:  выбираем каждый документ, заполняем его новыми данными и записываем. В старом варианте работы мы можем только сформировать по регистратору набор с заполненными данными и вызвать запись набора в режиме замещения данных, причем только по одному регистратору. Так придется делать 100 000 раз, что очень не быстро, единственный вариант ускорения - это распараллеливания записи документов. Если предположить, что при первоначальном заполнении нам нужно ставку НДС брать из Номенклатуры, НДС всегда в цене (сложные условия расчета, ограничение выборки, распараллеливание опускаем для наглядности), то базовый код по заполнению данных может выглядеть так:

 
 Код обработки в старом варианте

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

update НашаТаблица set СуммаНДС = ... where ....

приходится организовывать непростой алгоритм. Примером тому может служить архитектура расчета себестоимости в УТ/КА/ЕРП, где регламентной процедурой расчета себестоимости результат расчета дописывается к документам расхода (продажи) и возникает ровно такая же проблема - запись наборов регистра накопления в цикле. Новые операции как раз и призваны максимально уменьшить использование циклов при записи данных в СУБД. Далее рассмотри применимость трех новых вариантов замещения данных в наборах регистра.

 

Режим замещения Обновление

 

Для использования данного режима потребуется в наборе регистра включить расширенный режим замещения и сбросить отбор:

Набор = РегистрыНакопления.Продажи.СоздатьНаборЗаписей();
Набор.РасширенныеРежимыЗамещения = Истина;
Набор.Отбор.Сбросить(); // необходимо использовать так как у набора в структуре Отбор 
                        //свойство Использовать отбора Регистратор по умолчанию включено
	
//...Заполнение набора

Набор.Записать(РежимЗамещения.Обновление);

Получившийся набор - это просто записи, которые будут обновлены в таблице по совпадающим ключам (для регистра накопления это Регистратор и НомерСтроки), ранее разработчики не управляли полем НомерСтроки, а теперь его необходимо заполнять, чтобы уникально определить строку, которую потребуется обновить. По сути, свойство РасширенныеРежимыЗамещения переключает поле НомерСтроки в режим редактирования. Так же необходимо, чтобы при обновлении присутствовали все поля, выборочно обновить только нужные поля нельзя, код получится таким:

//Шаг 1 - читаем данные
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|	Продажи.Регистратор КАК Регистратор,
|	Продажи.НомерСтроки КАК НомерСтроки,
|	Продажи.Клиент КАК Клиент,
|	Продажи.Номенклатура КАК Номенклатура,
|	Продажи.Количество КАК Количество,
|	Продажи.Сумма КАК Сумма,
|	Продажи.Сумма - ВЫБОР
|		КОГДА Продажи.Номенклатура.СтавкаНДС = ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.Ставка10)
|			ТОГДА Продажи.Сумма / 1.1
|		КОГДА Продажи.Номенклатура.СтавкаНДС = ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.Ставка20)
|			ТОГДА Продажи.Сумма / 1.2
|       // ... Прочие ставки НДС   
|		ИНАЧЕ Продажи.Сумма
|	КОНЕЦ КАК СуммаНДС
|ИЗ
|	РегистрНакопления.Продажи КАК Продажи";

ВыборкаЗаписей = Запрос.Выполнить().Выбрать();

//Шаг 2 - записываем данные
НовыйНабор = РегистрыНакопления.Продажи.СоздатьНаборЗаписей();
НовыйНабор.РасширенныеРежимыЗамещения = Истина;
НовыйНабор.Отбор.Сбросить();

Пока ВыборкаЗаписей.Следующий() Цикл
	
	НоваяЗапись = НовыйНабор.Добавить();
	ЗаполнитьЗначенияСвойств(НоваяЗапись, ВыборкаЗаписей);
	
КонецЦикла;

НовыйНабор.Записать(РежимЗамещения.Обновление);

Код стал выглядеть проще и наглядней и теперь хочется увидеть, что он ровно делает то, что от него ожидается. По технологическому журналу соберем события работы с СУБД и с управляемыми блокировками, на небольшом объеме данных (2 документа по 6 номенклатур в каждом) увидим следующее:

 
 Часть технологического журнала, показывающая обновление большого количества строк

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

Теперь попробуем сделать тоже самое с замером времени, сгенерируем 100 000 документов, в каждом также по 6 товаров. Итого в регистре накопления Продажи будет 600 000. Запустим обновление и получим такие характеристики:

Количество записей к обновлению: 600 000.

Время выполнения транзакции: 42 сек.

Количество обращений к СУБД: 424, время работы запросов составило 23 сек.

Остается  19 сек, на некие события, да и количество запросов интуитивно много, посмотрим, что в технологическом журнале.

 
 Куда ушло 19 сек?

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

Режим замещения Удаление

 

Для режима замещения Удаление так же потребуется установка свойства набора РасширенныеРежимыЗамещения в Истину и сбросить отбор, а далее необходимо заполнить набор строками-ключами для удаления. После удаления может нарушиться нумерация строк в наборе, но платформа берет на себя этот момент и выровняет номера по порядку, поэтому в документации делается аккуратное замечание, что не стоит строить логику на номерах строк набора записей подчиненного регистратору. Чтобы протестировать данный функционал и посмотреть, что происходить на уровне СУБД, реализуем следующий код: предположим, что при формировании допустили ошибку и каждая строка в табличной части была задублирована по порядку при записи в регистр, тогда получается, что в наборе каждая четная строка ошибочная, которую нужно удалить. Новый подход к работе с наборами позволяет избежать множественных перезаписей набора, код для удаления будет выглядеть так:

//Шаг 1 - читаем данные
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	Продажи.Регистратор КАК Регистратор,
|	Продажи.НомерСтроки КАК НомерСтроки
|ИЗ
|	РегистрНакопления.Продажи КАК Продажи
|ГДЕ
|	Продажи.НомерСтроки / 2 = ЦЕЛ(Продажи.НомерСтроки / 2)";

РезультатЗапроса = Запрос.Выполнить().Выбрать();

//Шаг 2 - формируем набор к удалению и удаляем данные
Набор = РегистрыНакопления.Продажи.СоздатьНаборЗаписей();
Набор.РасширенныеРежимыЗамещения = Истина;
Набор.Отбор.Сбросить();

Пока РезультатЗапроса.Следующий() Цикл
	
	НоваяЗапись = Набор.Добавить();
	ЗаполнитьЗначенияСвойств(НоваяЗапись, РезультатЗапроса);
	
КонецЦикла;

Набор.Записать(РежимЗамещения.Удаление);

Результат работы по технологическому журналу:

 
 Часть технологического журнала, показывающая SQL запросы по удалению строк 

По запросам видно, что проверки существования ключей перед удалением не происходит, в отличие от режима Обновление. Так же виден один запрос, по обновлению нумерации строк. Временная таблица, по которой определяется на сколько (Shift) надо уменьшить номер строки, чтобы вернуться к порядку, рассчитывается на стороне платформы.

Проверим то же самое на 600 000 записях, будем также удалять четные позиции, получаем:

Количество записей к удалению: 300 000.

Время выполнения транзакции: 7.9 сек., время работы запросов: 7.1 сек. 

 
 Часть технологического журнала при удалении большого количества строк

Удаление большого объема без вложенных циклов, практически все потраченное время - это исполнение запросов, что и ожидается.

 

Режим замещения Слияние

 

В результате слияния будет выполнено две операции: обновлены строки, совпадающие по ключам, и добавлены записи, которые не были найдены по ключам. Не работает для регистров, подчиненных регистратору, поэтому на нашем примере с регистром накопления не проверить. Сделаем регистр сведений, который будет хранить оперативный остаток по номенклатуре в разрезе складов и каждый раз, когда будет делать некий документ делать движения по складу мы будет производить расчет текущий остатков и записывать в данных регистр сведений. Такая таблица позволит эффективно строить рабочие места с динамическими списками, где нужно отражать текущий остаток по складу. Данная таблица по сути аналогична регистру сведений РаспределениеЗапасов из ЕРП/КА/УТ систем, который показывает текущий срез по запасам (резерв, остаток, доступность и т.д.). Режим замещения Слияние позволит избавиться от множества наборов, которые надо записать, обновляя и дополняя остатки по списку номенклатуры.

 

рис 3. Структура регистра оперативных остатков

 

Проверку организуем следующим образом, предварительно заполним регистр ОперативныеОстатки частью данных, чтобы были совпадающие ключ для обновления, а затем в режиме замещения Слияние добавим записи по всем номенклатурам. Псевдокод использования режима замещения Слияние:

Набор = РегистрыСведений.ОперативныеОстатки.СоздатьНаборЗаписей();
//РасширенныеРежимыЗамещения - указывать не нужно
//Отбор по умолчанию пустой

//... Заполняем набор данными

//Записываем набор
Набор.Записать(РежимЗамещения.Слияние);

Результат работы по технологическому журналу:

 
 Часть технологического журнала, показывающая SQL запросы по слиянию записей в регистре

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

Теперь попробуем произвести слияние остатков по всем уникальным номенклатурам, количество таких 28 434, количество записей в регистре до слияния 10 004. Таким образом, 10 004 должны быть обновление и 18 430 будут добавлены.

Результат работы по технологическому журналу:

 
 Часть технологического журнала, слияние большого объема данных

 

Время выполнения транзакции: 1.75 сек., время работы запросов составило 0.16 сек.

В данном случае, слияние записей отрабатывает заметно быстро. При этом самая длительная операция чуть более 1 сек - это наложение управляемой блокировки на 28 434 строк по комбинациям измерений склада и номенклатуры.

Итого

 

  1. Для регистра сведений, не подчиненному регистратору, доступны все три новых режима замещения: Обновление, Удаление, Слияние. Для подчиненных регистров в расширенном режиме замещения только два: Обновление, Удаление.
  2. Чем проще структура регистра, тем эффективнее будет массовая обработка за счет уменьшения накладных расходов на поддержании нумерации строк в подчиненном регистре и уменьшении запросов для поддержания консистенции в таблицах итогов.
  3. В новых трех режимах поле свойство набора Отбор теряет смысл.
  4. Режим замещение Обновление самый медленный вариант работы из-за внутреннего алгоритма, который разбивает регистраторы на блоки по 256 штук и циклично выполняем по ним запросы к СУБД. Остальные режимы выполняют запросы по сути в одном запросе и в целом по результатам тестов достаточно эффективны.
  5. Стоит контролировать объем изменяемых данных и не забывать про эскалацию на управляемых блокировках, что может привести к блокировке всей таблицы и заблокировать работу пользователей. Так же стоит большие объемы данных дробить так, чтобы время транзакции не было большим, иначе это уже может сказываться на работу всей системы, особенно на PostgreSQL, где длинная транзакция может блокировать работу autovacuum, что приведет к деградации работы всего кластера.
  6. При использовании режимов замещения платформа сама позаботиться о выполнении действий в транзакции и расставит необходимые управляемые блокировки.
  7. Выявлено два недочета в режиме замещения Обновление, снижающие производительность: работа алгоритма блоками по 256 регистраторов, установка лишней управляемой блокировки.
  8. Стоит ожидать в типовых конфигурация ускорения расчета себестоимости на этапе записи расчета, а так же ускорение работы подсистем взаиморасчеты онлайн, распределение запасов и всех динамических списков, которые являются отражением сложного представления данных, использующие отложенное отражение данных, например, регистр Реестр документов в УТ/КА/ЕРП, используемый для отображения документов продажи и закупок.

Вступайте в нашу телеграмм-группу Инфостарт

Режимы замещения слияние удаление обновление массовая обработка пакетная обработка ускорение записи наборов

См. также

Механизмы платформы 1С Программист Бесплатно (free)

Разберем 15 мифов о работе платформы «1С:Предприятие 8» – как распространенных, так и малоизвестных. Начнем с классики: «Код, написанный в одну строку, работает быстрее, чем многострочный». Так ли это на самом деле?

16.07.2025    21434    TitanLuchs    106    

136

Механизмы платформы 1С Работа с интерфейсом Программист Стажер 1С v8.3 Бесплатно (free)

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

03.02.2025    12262    bayselonarrend    126    

64

Механизмы платформы 1С WEB-интеграция Программист 1С v8.3 Бесплатно (free)

В платформе 8.3.27 появилась возможность использовать WebSocket-клиент. Давайте посмотрим, как это все устроено и чем оно нам полезно.

14.01.2025    20879    dsdred    77    

134

Механизмы платформы 1С Программист Стажер 1С v8.3 1C:Бухгалтерия Бесплатно (free)

Эта небольшая статья - некоторого рода шпаргалка по файловым потокам: как и зачем с ними работать, какие преимущества это дает.

23.06.2024    21083    bayselonarrend    22    

169

Механизмы платформы 1С Программист Стажер 1С v8.3 1C:Бухгалтерия Бесплатно (free)

Пример использования «Сервисов интеграции» без подключения к Шине и без обменов.

13.03.2024    11860    dsdred    22    

84

Механизмы платформы 1С Программист Стажер 1С v8.3 Бесплатно (free)

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

24.01.2024    43318    YA_418728146    35    

75
Для отправки сообщения требуется регистрация/авторизация