Запись данных в независимый регистр сведений: менеджер записи, набор и массовая загрузка

· 9 мин

Содержание

Добавить одну запись в независимый регистр сведений проще всего через менеджер записи: РегистрыСведений.ИмяРегистра.СоздатьМенеджерЗаписи(), заполнить измерения и ресурсы, вызвать Записать(). Для пачки записей берут набор: СоздатьНаборЗаписей(), устанавливают отбор, добавляют строки методом Добавить() и пишут Записать(Ложь), чтобы не стереть то, что уже лежит в регистре.

Главная ловушка — параметр Замещать. По умолчанию он равен Истина, и набор без отбора при записи затирает весь регистр. Код ниже рассчитан на платформу 8.3 и работает одинаково в любой конфигурации: БП 3.0, ЗУП 3.1, УТ 11 или самописной.

Чем независимый регистр отличается от подчинённого

Режим записи задаётся в свойствах регистра в Конфигураторе, на закладке «Данные» или в палитре свойств: РежимЗаписи = Независимый или ПодчинениеРегистратору. От этого зависит, кто имеет право писать данные.

ПризнакНезависимыйПодчинённый регистратору
Кто пишетЛюбой код, обработка, пользователь вручнуюТолько документ-регистратор при проведении
Ключ записиИзмерения (+ период, если регистр периодический)Регистратор + номер строки
Менеджер записиЕстьНет
Отбор набораПо любым измерениям и периодуТолько по регистратору
Удаление записейЯвно, кодом или вручнуюВместе с отменой проведения

Из таблицы следуют три практических вывода:

  • 🔑 Уникальность по ключу. В регистре не может быть двух записей с одинаковым набором измерений и периодом. Повторная запись с тем же ключом либо заменит старую, либо упадёт с ошибкой — смотря как вы пишете.
  • 🧩 Измерения — это ключ, ресурсы — значение. Реквизиты регистра в ключ не входят, их меняют вместе с ресурсами.
  • 🛠 Вручную тоже можно. Пользователь с правом на изменение открывает список регистра и нажимает «Создать». Если регистр не выведен в интерфейс, его находят через Главное меню → Все функции → Регистры сведений. Если пункта нет, включите его в параметрах: Главное меню → Сервис → Параметры, флажок «Отображать команду “Все функции”».

Код записи выполняйте на сервере: в модуле формы — в процедуре с директивой &НаСервере или &НаСервереБезКонтекста, в общем модуле — с признаком «Сервер». На тонком клиенте менеджеры регистров недоступны.

Одна новая запись через менеджер записи

Менеджер записи работает ровно с одной строкой регистра. Он удобен, когда нужно положить или поправить одно значение: настройку, ставку, соответствие.

Пример: непериодический регистр СоответствиеСкладов с измерением Склад и ресурсом СкладВЕРП.

&НаСервереБезКонтекста
Процедура ЗаписатьСоответствие(Склад, СкладВЕРП)

	МенеджерЗаписи = РегистрыСведений.СоответствиеСкладов.СоздатьМенеджерЗаписи();
	МенеджерЗаписи.Склад = Склад;
	МенеджерЗаписи.СкладВЕРП = СкладВЕРП;
	МенеджерЗаписи.Записать(); // Замещать = Истина по умолчанию

КонецПроцедуры

Что делает Записать() с разными параметрами:

ВызовЗаписи с таким ключом нетЗапись с таким ключом есть
Записать() или Записать(Истина)ДобавляетЗаменяет старую
Записать(Ложь)ДобавляетОшибка: запись с такими ключевыми полями уже существует

Если нужно именно «добавить, но не трогать существующее», сначала проверьте наличие записи:

МенеджерЗаписи = РегистрыСведений.СоответствиеСкладов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Склад = Склад;
МенеджерЗаписи.Прочитать();

Если МенеджерЗаписи.Выбран() Тогда
	// Запись уже есть — решаем, перезаписывать ли
	Возврат;
КонецЕсли;

МенеджерЗаписи.Склад = Склад; // ключ заполняем заново перед записью
МенеджерЗаписи.СкладВЕРП = СкладВЕРП;
МенеджерЗаписи.Записать();

Метод Прочитать() ищет запись по значениям измерений (и периода), которые уже заполнены в менеджере. Выбран() возвращает Истина, если запись нашлась. Ключ перед записью надёжнее заполнить повторно: так код не зависит от того, в каком состоянии менеджер остался после неудачного чтения, и строка с пустым измерением не попадёт в регистр.

Периодический регистр: период, срезы и запись на дату

Периодичность выбирают в свойствах регистра: «В пределах секунды», «дня», «месяца», «квартала», «года». У независимого регистра вариант «По позиции регистратора» недоступен. В периодическом регистре поле Период входит в ключ записи.

Пример: регистр ЦеныПоставщиков, периодичность «В пределах дня», измерения Поставщик и Номенклатура, ресурс Цена.

МенеджерЗаписи = РегистрыСведений.ЦеныПоставщиков.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = '20261001';
МенеджерЗаписи.Поставщик = Поставщик;
МенеджерЗаписи.Номенклатура = Номенклатура;
МенеджерЗаписи.Цена = 1250;
МенеджерЗаписи.Записать();

Что стоит помнить про период:

  • 📅 Платформа приводит дату к началу периода. При периодичности «В пределах дня» запись на 01.10.2026 15:40 ляжет на 01.10.2026 0:00:00. Две записи за один день с одинаковыми измерениями — это одна запись, вторая заменит первую.
  • 📈 История копится, а не перезаписывается. Новая цена на 15.10 не трогает цену на 01.10: обе записи живут в регистре.
  • 🔎 Актуальное значение берут срезом. Срез последних возвращает для каждого сочетания измерений последнюю запись на дату.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Цены.Номенклатура,
|	Цены.Цена
|ИЗ
|	РегистрСведений.ЦеныПоставщиков.СрезПоследних(&Дата, Поставщик = &Поставщик) КАК Цены";
Запрос.УстановитьПараметр("Дата", ТекущаяДатаСеанса());
Запрос.УстановитьПараметр("Поставщик", Поставщик);
Результат = Запрос.Выполнить().Выгрузить();

Для одного значения без запроса подойдёт метод менеджера регистра ПолучитьПоследнее(Дата, Отбор), где отбор — структура с измерениями. Он возвращает структуру ресурсов.

Условие на измерения пишите внутри параметров виртуальной таблицы СрезПоследних(...), а не в секции ГДЕ. Так платформа отбирает данные до вычисления среза, и запрос работает быстрее.

Массовая загрузка через набор записей

Когда записей сотни и тысячи, менеджер записи в цикле — медленный путь: каждая запись идёт отдельным обращением к базе. Набор записей пишет пачку за один вызов.

У набора есть отбор. От него зависит, какие записи будут заменены при Записать(Истина):

  • 🎯 отбор установлен — заменяются записи, попадающие под отбор;
  • 💥 отбор пустой — набор претендует на весь регистр, и запись с замещением удалит всё, чего нет в наборе.

Добавление к существующим данным

Набор = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
Набор.Отбор.Поставщик.Установить(Поставщик);
Набор.Отбор.Период.Установить('20261015');

Для Каждого Строка Из ТаблицаЦен Цикл
	Запись = Набор.Добавить();
	Запись.Период = '20261015';
	Запись.Поставщик = Поставщик;
	Запись.Номенклатура = Строка.Номенклатура;
	Запись.Цена = Строка.Цена;
КонецЦикла;

Набор.Записать(Ложь); // только добавить, ничего не замещая

Если среди добавляемых строк окажется ключ, который уже есть в регистре, Записать(Ложь) упадёт с ошибкой и не запишет ничего из набора.

Полная замена среза данных

Типичный сценарий — загрузить прайс поставщика на дату целиком, заменив старые цены этого поставщика на эту же дату:

Набор = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
Набор.Отбор.Поставщик.Установить(Поставщик);
Набор.Отбор.Период.Установить(ДатаПрайса);

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

Набор.Загрузить(ТаблицаЦен);
Набор.Записать(); // Замещать = Истина: старые записи под отбором заменятся

Метод Загрузить() принимает таблицу значений, у которой имена колонок совпадают с именами полей регистра. Лишние колонки игнорируются, недостающие поля останутся пустыми.

Каждая запись набора должна соответствовать отбору. Если в отборе стоит поставщик А, а в строке набора — поставщик Б, платформа откажется записывать набор.

Порции для очень больших объёмов

На 100 000 строк один огромный набор съест память сервера. Разбивайте загрузку на порции по значению измерения: один поставщик — один набор, один склад — один набор. Если естественного разбиения нет, пишите Записать(Ложь) порциями по 5–10 тысяч строк с пустым отбором, не забывая про Ложь.

Изменение и удаление записей

Изменить существующую запись можно обоими способами.

Через менеджер записи — прочитать, поменять ресурс, записать:

МенеджерЗаписи = РегистрыСведений.СоответствиеСкладов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Склад = Склад;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
	МенеджерЗаписи.СкладВЕРП = НовоеЗначение;
	МенеджерЗаписи.Записать();
КонецЕсли;

Если нужно поменять значение измерения, то есть сам ключ, — это уже другая запись. Менеджер после Прочитать() запоминает исходный ключ, поэтому смена измерения и Записать() переносят запись на новый ключ. Надёжнее явно: удалить старую и записать новую в одной транзакции.

Через набор — прочитать по отбору, обойти, записать обратно:

Набор = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
Набор.Отбор.Поставщик.Установить(Поставщик);
Набор.Прочитать();
Для Каждого Запись Из Набор Цикл
	Запись.Цена = Окр(Запись.Цена * 1.1, 2);
КонецЦикла;
Набор.Записать();

Удаление тоже двумя способами:

ЗадачаКак сделать
Удалить одну записьМенеджер записи: заполнить ключ, Прочитать(), Удалить()
Удалить все записи по условиюНабор: установить отбор, ничего не добавлять, Записать()
Очистить регистр целикомНабор без отбора, пустой, Записать()
// Удалить все цены поставщика
Набор = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
Набор.Отбор.Поставщик.Установить(Поставщик);
Набор.Записать();

Пустой набор без отбора стирает регистр полностью и без вопросов. Перед такой операцией в рабочей базе сделайте выгрузку .dt или резервную копию СУБД — восстановить записи из журнала регистрации не получится.

Ошибки при записи и как их ловить

Запись в регистр может упасть по нескольким причинам. Самые частые:

Диагностика: выберите симптом

Код массовой записи оборачивайте в транзакцию и обработку исключения:

НачатьТранзакцию();
Попытка
	Блокировка = Новый БлокировкаДанных;
	Элемент = Блокировка.Добавить("РегистрСведений.ЦеныПоставщиков");
	Элемент.УстановитьЗначение("Поставщик", Поставщик);
	Элемент.Режим = РежимБлокировкиДанных.Исключительный;
	Блокировка.Заблокировать();

	Набор.Записать();
	ЗафиксироватьТранзакцию();
Исключение
	ОтменитьТранзакцию();
	ЗаписьЖурналаРегистрации("Загрузка цен поставщиков",
		УровеньЖурналаРегистрации.Ошибка, , ,
		ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	ВызватьИсключение;
КонецПопытки;

Блокировка по измерению Поставщик не мешает другим сеансам писать цены других поставщиков. В режиме управляемых блокировок это стандартный приём.

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

Как ускорить запись в регистр

Медленная загрузка почти всегда упирается в одно из пяти мест.

  1. Менеджер записи в цикле. Замените на набор: тысяча записей одним Записать() в разы быстрее тысячи отдельных вызовов.
  2. Обработчики ПередЗаписью и ПриЗаписи модуля набора. При загрузке из внешнего источника их можно отключить: Набор.ОбменДанными.Загрузка = Истина;. Проверки и подписки на события при этом не выполняются — данные должны быть уже чистыми.
  3. Одна большая транзакция. Миллион строк в одной транзакции держит блокировки и раздувает журнал СУБД. Фиксируйте порциями.
  4. Итоги срезов. Если у регистра включены свойства «Разрешить итоги среза последних» или «среза первых», каждая запись дополнительно пересчитывает служебную таблицу. Это ускоряет чтение, но замедляет запись. Если регистр в основном пишут, а срез читают редко, эти свойства лучше не включать.
  5. Запрос внутри цикла. Проверка «есть ли такая запись» запросом на каждую строку убивает скорость. Получите существующие ключи одним запросом заранее и сверяйте в памяти через Соответствие или индекс таблицы значений.
// Порционная запись с отключёнными обработчиками
РазмерПорции = 5000;
Набор = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
Набор.ОбменДанными.Загрузка = Истина;

Для Каждого Строка Из ТаблицаЦен Цикл
	ЗаполнитьЗначенияСвойств(Набор.Добавить(), Строка);
	Если Набор.Количество() = РазмерПорции Тогда
		Набор.Записать(Ложь);
		Набор.Очистить();
	КонецЕсли;
КонецЦикла;

Если Набор.Количество() > 0 Тогда
	Набор.Записать(Ложь);
КонецЕсли;

ЗаполнитьЗначенияСвойств переносит значения колонок строки в одноимённые поля записи, поэтому названия колонок таблицы должны совпадать с измерениями и ресурсами регистра.

Перед запуском массовой загрузки
Выполнено: 0 / 5

Частые вопросы

Можно ли записать в независимый регистр из клиентского кода?
Нет. Менеджеры записи и наборы доступны на сервере и в толстом клиенте. В управляемой форме вызывайте серверную процедуру с директивой &НаСервере.
Как открыть форму конкретной записи регистра?
Создайте ключ записи: РегистрыСведений.ИмяРегистра.СоздатьКлючЗаписи(Новый Структура("Склад", Склад)) и передайте его в параметр Ключ при открытии формы записи.
Почему в периодическом регистре пропала запись, внесённая днём раньше?
Скорее всего, она не пропала, а была замещена: при периодичности «В пределах месяца» или «года» все записи одного месяца с теми же измерениями ложатся на одну дату и заменяют друг друга.
Можно ли сделать независимый регистр подчинённым, когда в нём уже есть данные?
Технически режим записи меняется в Конфигураторе, но при реструктуризации записи без регистратора будут потеряны. Сначала выгрузите данные, затем меняйте режим и перезаписывайте их документами.
Сработает ли Записать(Ложь), если записи с таким ключом совпадают полностью?
Нет. Платформа сравнивает только ключ — измерения и период. Даже при одинаковых ресурсах повторный ключ даст ошибку.