Запись в регистр сведений 1С программно: менеджер записи, набор записей и удаление
Запись в регистр сведений из кода — частая задача при загрузке цен, обменах и в регламентных заданиях. В статье — готовые примеры для менеджера записи и набора записей, удаление и полная очистка регистра, чтение через срез последних и разбор ошибки о неуникальных записях.
Задача: записать, изменить и удалить данные регистра сведений из кода
Регистр сведений хранит значения в разрезе измерений: цены номенклатуры, курсы валют, настройки по организациям, статусы документов. Пользователь может править такие записи вручную, но чаще их заполняет код — при загрузке из файла, при обмене, в регламентном задании или при проведении документа. Ниже разобраны все варианты записи в регистр сведений программно, удаление и очистка, чтение, а также причины ошибки «Записи регистра сведений стали неуникальными».
В примерах используются два условных регистра: периодический ЦеныНоменклатуры (измерения Номенклатура и ВидЦены, ресурс Цена, периодичность «День») и непериодический НастройкиПользователей (измерения Пользователь и Настройка, ресурс Значение).
Какой бывает регистр сведений
От настроек регистра зависит, каким способом в него можно писать:
- Независимый (режим записи «Независимый») — записи создаются и меняются произвольно: через менеджер записи, набор записей или вручную в форме списка.
- Подчинённый регистратору — каждая запись принадлежит документу-регистратору. Такие записи пишутся только набором с отбором по регистратору, обычно через движения документа.
- Периодический — у записей есть поле
Период, и регистр хранит историю значений. Периодичность (секунда, день, месяц и так далее) определяет, с какой точностью хранится период. Периодическим может быть как независимый, так и подчинённый регистр.
Ключ записи — это совокупность измерений, а для периодического регистра ещё и периода (для подчинённого — регистратора и номера строки). Двух записей с одинаковым ключом быть не может, отсюда большинство ошибок при записи.
Менеджер записи: одна запись по ключу
Менеджер записи удобен, когда нужно создать, изменить или удалить ровно одну запись независимого регистра. Его получают методом СоздатьМенеджерЗаписи().
&НаСервере
Процедура УстановитьЦену(Номенклатура, ВидЦены, Цена, ДатаЦены)
МенеджерЗаписи = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = НачалоДня(ДатаЦены);
МенеджерЗаписи.Номенклатура = Номенклатура;
МенеджерЗаписи.ВидЦены = ВидЦены;
МенеджерЗаписи.Цена = Цена;
МенеджерЗаписи.Записать(); // Замещать = Истина по умолчанию
КонецПроцедуры
Параметр Записать(Замещать) по умолчанию равен Истина: если запись с таким ключом уже есть, она будет перезаписана. С Записать(Ложь) менеджер попытается добавить новую запись, и при совпадении ключа платформа выдаст ошибку о существующей записи.
Чтобы изменить только часть ресурсов, существующую запись сначала читают: заполняют ключевые поля, вызывают Прочитать() и проверяют Выбран().
МенеджерЗаписи = РегистрыСведений.НастройкиПользователей.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Пользователь = Пользователь;
МенеджерЗаписи.Настройка = "ОсновнойСклад";
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
МенеджерЗаписи.Значение = Склад;
МенеджерЗаписи.Записать();
КонецЕсли;
Набор записей регистра сведений
Набор записей — основной инструмент для массовой записи. Он работает с группой записей, отобранных по значениям измерений, и при записи по умолчанию замещает все записи, попадающие под отбор. Это и удобно, и опасно: без отбора набор относится ко всему регистру.
&НаСервере
Процедура ЗаписатьЦеныНаДату(ВидЦены, ДатаЦены, ТаблицаЦен)
НаборЗаписей = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ВидЦены.Установить(ВидЦены);
НаборЗаписей.Отбор.Период.Установить(НачалоДня(ДатаЦены));
Для Каждого СтрокаЦены Из ТаблицаЦен Цикл
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = НачалоДня(ДатаЦены);
НоваяЗапись.ВидЦены = ВидЦены;
НоваяЗапись.Номенклатура = СтрокаЦены.Номенклатура;
НоваяЗапись.Цена = СтрокаЦены.Цена;
КонецЦикла;
НаборЗаписей.Записать(); // заменит все цены этого вида на эту дату
КонецПроцедуры
Отбор в наборе задаёт, какие записи будут заменены. В примере набор перепишет все цены выбранного вида на указанную дату: позиции, которых нет в ТаблицаЦен, удалятся. Если нужно лишь добавить или обновить конкретные позиции, установите отбор ещё и по Номенклатура — и записывайте каждую позицию своим набором, либо используйте менеджер записи.
Чтобы дополнить существующие записи, набор сначала читают методом Прочитать(), меняют или добавляют строки и записывают. Вызов Записать(Ложь) добавляет записи набора к существующим без удаления, но при совпадении ключа хотя бы одной записи запись завершится ошибкой.
НаборЗаписей = РегистрыСведений.НастройкиПользователей.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Пользователь.Установить(Пользователь);
НаборЗаписей.Прочитать();
Для Каждого Запись Из НаборЗаписей Цикл
Если Запись.Настройка = "ОсновнойСклад" Тогда
Запись.Значение = Склад;
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать();
При массовой загрузке — из файла, при обмене между базами, при начальном заполнении — у набора можно установить НаборЗаписей.ОбменДанными.Загрузка = Истина перед вызовом Записать(). В этом режиме платформа не вызывает обработчики событий ПередЗаписью и ПриЗаписи модуля набора записей, а значит, не выполняются проверки и дополнительные действия, заложенные разработчиками конфигурации. Пользуйтесь им осознанно: данные должны быть уже корректными, потому что проверять их больше никто не будет.
Запись в регистр, подчинённый регистратору
Для подчинённого регистра набор всегда работает с отбором по регистратору. В модуле документа это делают движения: Движения.ИмяРегистра уже содержит набор с нужным отбором. Вне документа отбор ставят явно.
НаборЗаписей = РегистрыСведений.СтатусыЗаказов.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Регистратор.Установить(ДокументСсылка); НоваяЗапись = НаборЗаписей.Добавить(); НоваяЗапись.Период = ДокументСсылка.Дата; НоваяЗапись.Заказ = ДокументСсылка; НоваяЗапись.Статус = Перечисления.СтатусыЗаказов.Отгружен; НаборЗаписей.Записать();
Удалить запись регистра сведений программно
Удалить одну запись независимого регистра можно менеджером записи: заполнить все поля ключа, прочитать и вызвать Удалить().
МенеджерЗаписи = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = НачалоДня(ДатаЦены);
МенеджерЗаписи.Номенклатура = Номенклатура;
МенеджерЗаписи.ВидЦены = ВидЦены;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
МенеджерЗаписи.Удалить();
КонецЕсли;
Группу записей удаляют пустым набором: устанавливают отбор и записывают набор, ничего в него не добавляя. Все записи под отбором будут удалены.
// Удалить все цены конкретной номенклатуры НаборЗаписей = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Номенклатура.Установить(Номенклатура); НаборЗаписей.Записать(); // Удалить движения документа по подчинённому регистру НаборЗаписей = РегистрыСведений.СтатусыЗаказов.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Регистратор.Установить(ДокументСсылка); НаборЗаписей.Записать();
Очистить регистр сведений полностью
Для независимого регистра полная очистка — это пустой набор без отбора. Код короткий, поэтому перед запуском стоит убедиться, что он выполняется именно в той базе и для того регистра, который нужно очистить.
НаборЗаписей = РегистрыСведений.НастройкиПользователей.СоздатьНаборЗаписей(); НаборЗаписей.Записать();
На больших регистрах удаление одной транзакцией создаёт длительную блокировку и нагружает СУБД. Безопаснее очищать порциями — например, по значениям первого измерения или по периодам: получить запросом список значений и записать пустой набор с отбором по каждому из них. Подчинённый регистр так очистить нельзя: его записи удаляют по регистраторам или перепроведением документов.
Чтение: срез последних и методы менеджера
Для периодического регистра актуальное значение на дату получают виртуальной таблицей СрезПоследних. Условие на измерения лучше передавать в параметры виртуальной таблицы, а не в ГДЕ — так СУБД сразу отбирает нужные записи.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
| &Дата,
| ВидЦены = &ВидЦены
| И Номенклатура В (&СписокТоваров)) КАК Цены";
Запрос.УстановитьПараметр("Дата", ТекущаяДатаСеанса());
Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
Запрос.УстановитьПараметр("СписокТоваров", СписокТоваров);
ТаблицаЦен = Запрос.Выполнить().Выгрузить();
Для одного значения без запроса подойдут методы менеджера регистра. ПолучитьПоследнее() возвращает структуру ресурсов на дату для периодического регистра, Получить() — ресурсы по ключу непериодического. Если запись не найдена, ресурсы в структуре будут пустыми.
Отбор = Новый Структура("Номенклатура, ВидЦены", Номенклатура, ВидЦены);
Ресурсы = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(ТекущаяДатаСеанса(), Отбор);
Цена = Ресурсы.Цена;
Ресурсы = РегистрыСведений.НастройкиПользователей.Получить(
Новый Структура("Пользователь, Настройка", Пользователь, "ОсновнойСклад"));
Склад = Ресурсы.Значение;
Метод ПолучитьПоследнее() в цикле по сотням позиций — это сотни отдельных обращений к базе. Для списков используйте запрос со срезом.
Ошибка «Записи регистра сведений стали неуникальными»
Ошибка означает, что после записи в регистре оказались бы две записи с одинаковым ключом. Типичные причины:
- Дубли внутри набора. В набор дважды добавлена одна и та же номенклатура на один период. Перед записью сверните исходную таблицу по измерениям или проверяйте наличие строки.
- Периодичность регистра. Для регистра с периодичностью «День» период приводится к началу дня, поэтому две записи на 10:00 и 15:00 одного дня совпадают по ключу.
- Запись с Замещать = Ложь.
Записать(Ложь)добавляет записи к существующим, и любой совпавший ключ вызывает ошибку.
Диагностика простая: перед Записать() выгрузите набор в таблицу методом НаборЗаписей.Выгрузить(), сверните по полям ключа с добавлением колонки-счётчика и найдите строки, где счётчик больше единицы.
Блокировки при записи из нескольких сеансов
Если один и тот же фрагмент регистра читают и записывают параллельно — например, регламентное задание и пользователь, — выполняйте чтение и запись в транзакции с управляемой блокировкой:
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.НастройкиПользователей");
ЭлементБлокировки.УстановитьЗначение("Пользователь", Пользователь);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
НаборЗаписей = РегистрыСведений.НастройкиПользователей.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Пользователь.Установить(Пользователь);
НаборЗаписей.Прочитать();
// ... изменение записей ...
НаборЗаписей.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Менеджер записи или набор записей
| Критерий | Менеджер записи | Набор записей |
|---|---|---|
| Количество записей | Одна по полному ключу | Любое под отбором |
| Подчинённый регистр | Не используется | Да, с отбором по регистратору |
| Удаление | Удалить() | Пустой набор с отбором |
| Массовая загрузка | Медленно | Оптимально |
| Риск | Перезапись одной записи | Удаление всего, что под отбором |
Актуальность для новых версий
Менеджер записи, набор записей и методы Получить(), ПолучитьПоследнее() работают одинаково во всех версиях платформы 8.3, включая 8.3.2x. Код выполняется только на сервере. В конфигурациях с управляемыми блокировками (режим «Управляемый» или «Автоматический и управляемый») при параллельной работе блокировку нужно устанавливать явно через БлокировкаДанных, как в примере.
Частые вопросы
Чем Записать(Истина) отличается от Записать(Ложь)?
С Истина набор заменяет все записи, попадающие под отбор, а менеджер перезаписывает запись с тем же ключом. С Ложь записи добавляются к существующим, и совпадение ключа приводит к ошибке.
Как удалить одну запись регистра сведений?
Заполните в менеджере записи все поля ключа, вызовите Прочитать(), проверьте Выбран() и вызовите Удалить(). Для группы записей запишите пустой набор с отбором.
Как полностью очистить независимый регистр сведений?
Создайте набор записей без отбора и вызовите Записать(). На больших регистрах лучше очищать порциями — по значениям измерения или периодам.
Откуда ошибка «Записи регистра сведений стали неуникальными»?
В наборе есть дубли по ключу, периоды разных записей совпали после приведения к периодичности регистра, либо Записать(Ложь) добавляет запись с уже существующим ключом.