Запись данных в независимый регистр сведений: менеджер записи, набор и массовая загрузка
Содержание
Добавить одну запись в независимый регистр сведений
проще всего через менеджер записи: РегистрыСведений.ИмяРегистра.СоздатьМенеджерЗаписи(), заполнить измерения и ресурсы, вызвать Записать(). Для пачки записей берут набор: СоздатьНаборЗаписей(), устанавливают отбор, добавляют строки методом Добавить() и пишут Записать(Ложь), чтобы не стереть то, что уже лежит в регистре.
Главная ловушка — параметр Замещать. По умолчанию он равен Истина, и набор без отбора при записи затирает весь регистр. Код ниже рассчитан на платформу 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 или резервную копию СУБД — восстановить записи из журнала регистрации не получится.
Ошибки при записи и как их ловить
Запись в регистр может упасть по нескольким причинам. Самые частые:
Записать(Ложь), а ключ уже занят — в регистре или в двух строках самого набора. Либо проверьте наличие через Прочитать(), либо пишите с замещением, либо свёрните таблицу перед загрузкой: ТаблицаЦен.Свернуть("Номенклатура", "Цена") — но помните, что цены при этом сложатся.Отбор.…Установить().Код массовой записи оборачивайте в транзакцию и обработку исключения:
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.ЦеныПоставщиков");
Элемент.УстановитьЗначение("Поставщик", Поставщик);
Элемент.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
Набор.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("Загрузка цен поставщиков",
УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
Блокировка по измерению Поставщик не мешает другим сеансам писать цены других поставщиков. В режиме управляемых блокировок это стандартный приём.
Перехватывая исключение, не глушите его молча. Запишите подробности в журнал регистрации и пробросьте дальше через ВызватьИсключение, иначе вызывающий код решит, что загрузка прошла.
Как ускорить запись в регистр
Медленная загрузка почти всегда упирается в одно из пяти мест.
- Менеджер записи в цикле. Замените на набор: тысяча записей одним
Записать()в разы быстрее тысячи отдельных вызовов. - Обработчики
ПередЗаписьюиПриЗаписимодуля набора. При загрузке из внешнего источника их можно отключить:Набор.ОбменДанными.Загрузка = Истина;. Проверки и подписки на события при этом не выполняются — данные должны быть уже чистыми. - Одна большая транзакция. Миллион строк в одной транзакции держит блокировки и раздувает журнал СУБД. Фиксируйте порциями.
- Итоги срезов. Если у регистра включены свойства «Разрешить итоги среза последних» или «среза первых», каждая запись дополнительно пересчитывает служебную таблицу. Это ускоряет чтение, но замедляет запись. Если регистр в основном пишут, а срез читают редко, эти свойства лучше не включать.
- Запрос внутри цикла. Проверка «есть ли такая запись» запросом на каждую строку убивает скорость. Получите существующие ключи одним запросом заранее и сверяйте в памяти через
Соответствиеили индекс таблицы значений.
// Порционная запись с отключёнными обработчиками
РазмерПорции = 5000;
Набор = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
Набор.ОбменДанными.Загрузка = Истина;
Для Каждого Строка Из ТаблицаЦен Цикл
ЗаполнитьЗначенияСвойств(Набор.Добавить(), Строка);
Если Набор.Количество() = РазмерПорции Тогда
Набор.Записать(Ложь);
Набор.Очистить();
КонецЕсли;
КонецЦикла;
Если Набор.Количество() > 0 Тогда
Набор.Записать(Ложь);
КонецЕсли;
ЗаполнитьЗначенияСвойств переносит значения колонок строки в одноимённые поля записи, поэтому названия колонок таблицы должны совпадать с измерениями и ресурсами регистра.
Частые вопросы
Можно ли записать в независимый регистр из клиентского кода?
&НаСервере.Как открыть форму конкретной записи регистра?
РегистрыСведений.ИмяРегистра.СоздатьКлючЗаписи(Новый Структура("Склад", Склад)) и передайте его в параметр Ключ при открытии формы записи.