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