При разработке конфигураций на платформе 1С:Предприятие разработчики часто сталкиваются с методом Записать() у объектов РегистрСведенийМенеджерЗаписи и РегистрСведенийНаборЗаписей. В синтакс-помощнике указано, что необязательный параметр Замещать (булево) определяет режим замещения существующей записи. При значении Истина выполняется замещение, а при значении Ложь запись дописывается к уже существующим. У многих программистов возникает логичный вопрос: почему при вызове Записать(Ложь) с уже существующим набором ключевых полей возникает исключение «Запись с такими ключевыми полями уже существует», и в каких ситуациях этот режим действительно необходим?
Разберем детально логику работы параметра замещения, его влияние на транзакции и различия между объектами платформы.
Рассмотрим, как устроен первичный ключ в независимом регистре сведений. Ключом записи является совокупность значений всех измерений (а для периодических регистров — измерений и даты Период). В реляционной таблице СУБД физически не могут существовать две строки с полностью идентичным первичным ключом, так как это нарушает целостность данных.
Посмотрим на поведение метода Записать() менеджера записи при различных значениях параметра:
Проанализируем пример кода с созданием записи через менеджер:
НоваяЗапись = РегистрыСведений.СтатусПаллетоМест.СоздатьМенеджерЗаписи();
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.Документ = ОбъектАдресноеХранение;
НоваяЗапись.Сектор = Сектор;
НоваяЗапись.Ряд = Ряд;
НоваяЗапись.Паллет = Паллет;
НоваяЗапись.Ответственный = Ответственный;
НоваяЗапись.Причина = Причина;
НоваяЗапись.Заблокировано = Ложь;
// Попытка добавить запись без права перезаписи существующих данных
НоваяЗапись.Записать(Ложь);
Параметр Ложь используется для строгого контроля целостности данных, когда случайная перезапись ранее внесенной информации недопустима. Разберем основные практические задачи:
Замещать = Ложь гарантирует, что чужие или ранее зафиксированные данные не будут стерты повторным вызовом кода.Прочитать(), изменили ключевые измерения и вызвали Записать(Истина), платформа удалит старую запись (по исходному ключу) и создаст новую. Если же вызвать Записать(Ложь), старая запись сохранится, а новая добавится рядом (при условии уникальности нового ключа).Важно помнить об архитектурном ограничении платформы 1С: ошибка нарушения первичного ключа СУБД переводит текущую транзакцию в невалидное состояние. Если метод Записать(Ложь) завершился ошибкой внутри открытой транзакции, перехват через Попытка ... Исключение не спасет транзакцию — при попытке вызвать ЗафиксироватьТранзакцию() возникнет ошибка «Транзакция отменена».
Поэтому для проверки наличия записи в транзакционных операциях следует использовать предварительное чтение или запрос:
// Безопасная проверка наличия записи через менеджер
Менеджер = РегистрыСведений.СтатусПаллетоМест.СоздатьМенеджерЗаписи();
Менеджер.Период = ТекущаяДата();
Менеджер.Сектор = Сектор;
Менеджер.Ряд = Ряд;
Менеджер.Паллет = Паллет;
Менеджер.Прочитать();
Если Менеджер.Выбран() Тогда
// Запись уже существует, выполняем альтернативное действие
ВызватьИсключение "Запись на текущий период уже существует в базе!";
Иначе
Менеджер.Ответственный = Ответственный;
Менеджер.Заблокировано = Ложь;
Менеджер.Записать(Ложь);
КонецЕсли;
Объект РегистрСведенийМенеджерЗаписи применим исключительно к независимым регистрам сведений и работает только с одной строкой. Для подчиненных регистратору регистров, а также для пакетной обработки данных используется РегистрСведенийНаборЗаписей.
В наборе записей параметр Замещать работает следующим образом:
// Добавление строк в регистр сведений без удаления существующих записей регистратора
Набор = РегистрыСведений.ДвиженияТоваров.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
НоваяСтрока = Набор.Добавить();
НоваяСтрока.Период = ТекущаяДата();
НоваяСтрока.Номенклатура = СсылкаНоменклатура;
НоваяСтрока.Количество = 10;
// Запись дописывает строку без предварительной очистки движений по данному документу
Набор.Записать(Ложь);
Использование МенеджерЗаписи в цикле является неоптимальным подходом, так как генерирует отдельные обращения к СУБД для каждой строки. Для массового добавления или обновления информации всегда используйте РегистрСведенийНаборЗаписей с подготовленным массивом данных.