Программная перезапись регистра сведений в 1С
Содержание
Перезаписать запись регистра
сведений в 1С 8.3 можно двумя объектами: для независимого регистра — менеджером записи (СоздатьМенеджерЗаписи), для подчинённого регистратору — набором записей (СоздатьНаборЗаписей) с методом Записать(Истина). Параметр Истина означает «заместить»: старая запись с тем же ключом измерений исчезает, на её место встаёт новая.
От режима записи регистра зависит почти всё остальное — поэтому первый шаг всегда один: открыть регистр в конфигураторе и посмотреть свойство «Режим записи». Ниже разберём оба режима, массовое обновление через запрос, а также то, что чаще всего ломает запись: блокировки и необработанные исключения в цикле.
Как регистр хранит данные и почему это важно для записи
Регистр сведений хранит значения в разрезе измерений (ключ записи), ресурсов (сами значения) и необязательных реквизитов. Комбинация измерений уникальна — двух записей с одинаковым набором измерений в регистре быть не может. Именно на этом и строится перезапись: вы подставляете тот же ключ и платформа замещает строку.
Два свойства регистра определяют способ записи:
- 🔹 Режим записи «Независимый» — записи живут сами по себе, регистратора у них нет. Пишутся менеджером записи или независимым набором записей.
- 🔹 Режим записи «Подчинение регистратору» — каждая запись привязана к документу-регистратору. Пишутся только набором записей с отбором по регистратору.
- 🔹 Периодичность — если регистр периодический, в ключ добавляется измерение
Период. Без заполненного периода запись в периодический регистр не уйдёт.
Если не знаете режим и периодичность конкретного регистра — не гадайте. Откройте дерево конфигурации, найдите регистр в ветке «Регистры сведений» и посмотрите его свойства. От этого зависит, какой из примеров ниже вам подходит.
Выбрать способ помогает короткая схема.
Записать(Истина). Запись с таким же ключом заместится.Записать(Истина).Перезапись независимого регистра менеджером записи
Менеджер записи работает с одной записью по полному набору измерений. Алгоритм — задать ключ, задать ресурсы, записать.
Менеджер = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
Менеджер.Период = ТекущаяДата(); // только для периодического регистра
Менеджер.Номенклатура = ВыбраннаяНоменклатура;
Менеджер.ТипЦен = ВыбранныйТипЦен;
Менеджер.Цена = 1500;
Менеджер.Записать(Истина); // Истина — заместить существующую запись
Параметр метода Записать управляет поведением при коллизии ключа:
| Значение | Что делает платформа |
|---|---|
Истина | запись с таким ключом замещается; если её не было — добавляется |
Ложь | если запись с таким ключом уже есть, будет вызвано исключение |
Для «честной» перезаписи почти всегда нужен Истина. Ложь берут, когда нужно гарантированно добавить новую запись и упасть, если дубль по ключу уже существует.
Если новое значение зависит от старого (например, поднять цену на 10%), сначала прочитайте запись:
Менеджер = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
Менеджер.Период = Дата;
Менеджер.Номенклатура = Ном;
Менеджер.ТипЦен = Тип;
Менеджер.Прочитать();
Если Менеджер.Выбран() Тогда
Менеджер.Цена = Менеджер.Цена * 1.1;
Менеджер.Записать(Истина);
КонецЕсли;
Прочитать() подтянет запись по заданным измерениям, Выбран() покажет, нашлась ли она. Вызывать Записать без проверки Выбран() после чтения опасно: если записи не было, вы перезапишете её значениями по умолчанию.
Перезапись независимого регистра набором записей
Менеджер записи удобен для одной записи. Но независимый регистр тоже можно писать набором записей — и это выгодно, когда нужно разом заместить все записи по одному значению измерения. Отбор ставится не по регистратору, а по любому индексируемому измерению:
Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(Ном);
Набор.Прочитать();
Набор.Очистить();
НоваяЗапись = Набор.Добавить();
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.Номенклатура = Ном;
НоваяЗапись.ТипЦен = ТипРозница;
НоваяЗапись.Цена = 1500;
Набор.Записать(Истина);
Разница с менеджером записи принципиальная:
- 🔸 Менеджер оперирует одной записью по полному ключу измерений. Если задать не все измерения, запись не будет однозначной.
- 🔸 Набор оперирует группой записей по частичному отбору.
Записать(Истина)заместит все записи, попавшие под отбор, — удобно, чтобы переписать всю историю цен по номенклатуре одним вызовом.
Отбор набора можно ставить по нескольким измерениям сразу — тогда замещается только их пересечение. Это основной способ массовой перезаписи независимого регистра без поэлементного цикла.
Если нужно заместить все записи по номенклатуре — ставьте отбор только по Номенклатура. Если по конкретной паре «номенклатура + тип цен» — добавляйте в отбор оба измерения. Пустой набор с установленным отбором и Записать(Истина) просто удалит записи, попавшие под отбор.
Периодичность: как перезапись зависит от периода
У периодического регистра период входит в ключ, и от выбранной периодичности зависит, что именно вы перезаписываете. Периодичность задаётся в свойствах регистра и определяет, до какой «сетки» платформа округляет период записи:
| Периодичность | Что считается одной записью |
|---|---|
| В пределах секунды | записи с точностью до секунды различаются |
| В пределах дня | все записи за день схлопываются в одну по дате |
| В пределах месяца | ключом служит месяц, число внутри месяца роли не играет |
Из этого следует неочевидный эффект: если периодичность — «в пределах месяца», а вы пишете две записи за разные числа одного месяца с одинаковыми прочими измерениями, вторая запись заместит первую. Это не ошибка кода, а работа периодичности.
При перезаписи периодического регистра всегда задавайте Период осознанно. Запись с Период = ТекущаяДата() и запись с Период = НачалоМесяца(ТекущаяДата()) при периодичности «в пределах месяца» — это одна и та же запись.
Как перезаписать подчинённый регистратору регистр
У записей такого регистра есть измерение-регистратор (документ). Менеджер записи здесь бесполезен — работают только наборы записей. Типовой приём: взять набор по регистратору, очистить, заполнить заново и записать с замещением.
Набор = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Прочитать();
Набор.Очистить(); // убираем всё, что было по этому регистратору
НоваяЗапись = Набор.Добавить();
НоваяЗапись.Регистратор = ДокументСсылка;
НоваяЗапись.Период = ДокументСсылка.Дата;
НоваяЗапись.Номенклатура = Ном;
НоваяЗапись.Значение = 42;
Набор.Записать(Истина); // Истина — заместить все записи по отбору
Ключевой момент — метод Записать у набора:
- 📌
Записать(Истина)— платформа удалит все записи, попадающие под текущий отбор, и запишет то, что сейчас в наборе. Это и есть перезапись «начисто». - 📌
Записать(Ложь)— записи из набора добавятся к уже существующим, ничего не удаляя.
Отбор обязательно должен совпадать с тем, по которому вы читали. Если открыть пустой набор с отбором по регистратору и записать его с Истина, все записи этого регистратора будут удалены — это штатный способ «очистить движения» документа.
Запись набора с замещением — операция «всё или ничего» в пределах отбора. Перед массовой чисткой движений в рабочей базе убедитесь, что отбор сужен до нужного регистратора, иначе рискуете снести чужие данные.
Массовая перезапись регистра сведений запросом
Когда нужно пересчитать сотни и тысячи записей, поэлементная работа «прочитал-изменил-записал» слишком медленна. Схема другая: выбрать данные запросом, а потом в цикле писать подготовленные значения.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры КАК Цены
|ГДЕ
| Цены.ТипЦен = &СтарыйТип";
Запрос.УстановитьПараметр("СтарыйТип", СтарыйТип);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Менеджер = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
Менеджер.Период = ТекущаяДата();
Менеджер.Номенклатура = Выборка.Номенклатура;
Менеджер.ТипЦен = НовыйТип;
Менеджер.Цена = Выборка.Цена * 1.05;
Менеджер.Записать(Истина);
КонецЦикла;
Запрос здесь нужен, чтобы не читать регистр внутри цикла: вся выборка готовится один раз, а цикл только пишет. Это разница в разы по времени на больших объёмах.
Для подчинённых регистров массовую перезапись удобнее вести по регистраторам: запрос возвращает список документов, а в цикле каждый регистратор перезаписывается своим набором. Один набор на регистратор — одна транзакция, предсказуемые блокировки.
Блокировки и обработка ошибок при записи
Любая запись регистра выполняется в транзакции. Если пишете партию записей и хотите, чтобы при сбое откатилась вся партия, оберните её в явную транзакцию с обработкой исключения:
НачатьТранзакцию();
Попытка
Для Каждого Элемент Из Партия Цикл
Менеджер = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
Менеджер.Период = ТекущаяДата();
Менеджер.Номенклатура = Элемент.Номенклатура;
Менеджер.ТипЦен = Элемент.ТипЦен;
Менеджер.Цена = Элемент.Цена;
Менеджер.Записать(Истина);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("Перезапись цен",
УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
Правила, которые убирают большинство проблем:
- ✅ После
ОтменитьТранзакцию()всегда либо пробрасывайте исключение (ВызватьИсключение), либо корректно завершайте обработку. Проглатывать ошибку молча нельзя. - ✅ Текст ошибки берите из
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())— он содержит причину отказа записи. - ✅ Не держите открытую транзакцию на время долгих вычислений: чем короче транзакция, тем меньше конфликтов блокировок.
Если в базе включён управляемый режим блокировок, перед чтением-изменением-записью ставьте управляемую блокировку явно — иначе возможны конфликты при параллельной работе пользователей:
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.ЦеныНоменклатуры");
Элемент.Режим = РежимБлокировкиДанных.Исключительный;
Элемент.УстановитьЗначение("Номенклатура", Ном);
Блокировка.Заблокировать();
Частая ошибка записи — «Запись с указанными ключевыми полями уже существует». Она возникает, когда менеджер пишут с параметром Ложь, а запись по ключу уже есть. Решение простое: если задача — именно перезаписать, ставьте Истина.
Как ускорить массовую перезапись
На больших объёмах важно не само количество записей, а число обращений к базе и длина транзакций. Что реально помогает:
- ⚡ Пишите наборами, а не по одной записи. Для подчинённых регистров один
Записать(Истина)на регистратор заменяет десятки отдельных записей — это одна операция вместо многих. - ⚡ Дробите на партии. Обрабатывайте по 500–1000 записей в одной транзакции, а не весь массив целиком: короткая транзакция реже упирается в блокировки и не раздувает журнал.
- ⚡ Готовьте данные запросом заранее. Никакого чтения регистра внутри цикла записи.
- ⚡ Отключайте лишнюю логику на время загрузки. Свойство
ОбменДанными.Загрузка = Истинау набора/менеджера отключает проверки и обработчики, которые на обычной записи не нужны при массовом переносе данных. - ⚡ Индексируйте измерение отбора. Если регулярно перезаписываете записи с отбором по измерению, у которого в конфигураторе не включена индексация, чтение набора будет идти по полному перебору. Включите «Индексировать» у этого измерения — отбор станет выборкой по индексу.
Набор.ОбменДанными.Загрузка = Истина;
Набор.Записать(Истина);
ОбменДанными.Загрузка = Истина снимает контроль со стороны кода подписок и проведения. Пользуйтесь им только для технической загрузки заведомо корректных данных, а не как «кнопкой ускорения» в обычных операциях.
Длинную перезапись запускайте фоновым заданием, а не в клиентском сеансе: так пользователь не ждёт у замороженной формы, а длительная блокировка не мешает остальным. И перед массовой операцией в рабочей базе сделайте резервную копию — откатить тысячи перезаписанных строк иначе нечем.
Отдельно стоит решить, нужна ли перезапись вообще. Если задача — просто добавить записи, которых ещё нет, менеджер с параметром Истина подойдёт и так, без предварительного чтения. А если вы меняете одно ресурсное значение у одной записи, набор записей с полной перезаписью по отбору избыточен — достаточно менеджера. Перезапись набором оправдана там, где нужно заместить группу записей целиком.
В чём разница между менеджером записи и набором записей?
Что делает параметр `Истина` в методе `Записать`?
Почему запись в периодический регистр не сохраняется?
Период. Для периодического регистра период входит в ключ: без него платформа не знает, какую запись замещать.Как удалить движения документа по регистру сведений?
Записать(Истина). Пустой набор заместит записи регистратора — они удалятся.