Почему перезаписываются строки в независимом регистре сведений 1С и как устранить ошибку уникальности

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке функционала хранения связанных данных (например, адресов доставки контрагентов) начинающие разработчики нередко выбирают независимый непериодический регистр сведений. Однако при попытке программного добавления записей возникает типичная проблема: при одинаковом составе измерений запись либо затирает предыдущую, либо система выдает ошибку о нарушении уникальности данных. Разберем внутренние механизмы платформы 1С, выясним причину такого поведения и рассмотрим корректные пути решения.

Причина поведения: первичный ключ и параметры метода Записать()

Проанализируем структуру регистра сведений в реляционной СУБД. В непериодическом независимом регистре сведений совокупность всех измерений формирует первичный ключ таблицы базы данных. Это фундаментальное правило архитектуры 1С: на одно уникальное сочетание значений измерений может приходиться только один набор ресурсов и реквизитов. Платформа физически не может хранить две разные строки, у которых полностью совпадают значения всех измерений.

Рассмотрим, что происходит при выполнении типового программного кода работы с объектом НаборЗаписей:


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

НоваяЗапись = НаборЗаписей.Добавить(); 
НоваяЗапись.Объект = Стр.Объект;  
НоваяЗапись.Перевозчик = Стр.Перевозчик;
НоваяЗапись.Получатель = Стр.Получатель;
НоваяЗапись.АдресПолучателя = Стр.АдресПолучателя;

НаборЗаписей.Записать(Ложь);

Разберем различия в поведении в зависимости от параметра замещения метода Записать():

  1. Вызов НаборЗаписей.Записать(Истина) (или без параметров): параметр Замещение = Истина очищает в базе данных все существующие записи, попадающие под установленный отбор, и записывает те строки, которые находятся в наборе в памяти. Если перед добавлением строки не был вызван метод НаборЗаписей.Прочитать(), старые данные безвозвратно удаляются, и в регистре остается только одна вновь созданная строка.
  2. Вызов НаборЗаписей.Записать(Ложь): флаг замещения выключен, и платформа пытается вставить новую запись в таблицу СУБД без предварительного удаления старых строк. Если запись с такой комбинацией измерений уже присутствует, база данных возвращает ошибку нарушения уникальности первичного ключа.

Вариант 1. Введение дополнительного уникального измерения

Если для бизнес-логики принципиально хранить несколько разных адресов для одной и той же связки контрагента, перевозчика и получателя именно в регистре сведений, необходимо обеспечить уникальность ключа записи. Для этого в состав измерений добавляют еще одно поле.

Существует два основных способа формирования такого измерения:

  1. Числовой номер (НомерЗаписи): измерение с типом Число. Главный недостаток этого метода — отсутствие встроенного автоинкремента в регистрах сведений 1С. Разработчику приходится самостоятельно вычислять следующий свободный номер запросом к базе (через функцию МАКСИМУМ(НомерЗаписи) + 1). В многопользовательской среде это требует явной блокировки диапазона регистра через объект БлокировкаДанных, иначе при одновременной записи возникнет коллизия номеров.
  2. Идентификатор строки (ИдентификаторЗаписи): измерение с типом Строка (длиной 36 символов). При создании новой строки генерируется уникальный строковый GUID с помощью конструкции Строка(Новый УникальныйИдентификатор()). Такой подход полностью исключает риск дублирования ключей и не требует наложения тяжелых блокировок для расчета очередного номера.

Ограничение длины строковых измерений

Обратим внимание на типы данных измерений. Использование неограниченных или слишком длинных строк в измерениях — грубая ошибка проектирования. В большинстве СУБД (например, Microsoft SQL Server) максимальный размер индекса первичного ключа ограничен 900 байтами. Если суммарная длина всех строковых измерений превысит допустимый предел, запись в регистр завершится аварийной системной ошибкой. Все длинные описательные поля (адреса, телефоны, комментарии) всегда должны размещаться в ресурсах или реквизитах регистра.

Вариант 2. Использование объекта МенеджерЗаписи для точечных изменений

Когда требуется изменить или перезаписать строго одну запись регистра, использование набора записей с фильтрами часто бывает избыточным. Для работы с единичной записью в платформе 1С предусмотрен объект МенеджерЗаписи.

Посмотрим на пример правильного чтения и изменения строки через менеджер:


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

// Считываем текущую запись из базы данных
Менеджер.Прочитать();

Если Менеджер.Выбран() Тогда
    // Запись найдена, обновляем нужный ресурс
    Менеджер.ТелефонПолучателя = Стр.ТелефонПолучателя;
    Менеджер.Записать();
КонецЕсли;

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

Вариант 3. Архитектурная альтернатива: выбор другого объекта метаданных

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

  1. Подчиненный справочник: создадим справочник АдресаДоставки, подчиненный справочнику Контрагенты. Справочник обеспечивает уникальность каждой записи за счет собственной ссылки (UUID). В нем можно хранить любое количество одинаковых по содержанию адресов, добавлять предопределенные реквизиты и стандартно выводить список на форму владельца без ручного программного кода записи наборов.
  2. Табличная часть справочника: если адреса доставки используются исключительно в контексте конкретного контрагента и к ним не требуется обращаться из внешних аналитических отчетов независимо от владельца, самым простым решением будет добавление табличной части АдресаДоставки непосредственно в справочник Контрагенты. Это полностью снимает вопросы транзакционной записи и синхронизации табличных полей формы.
  3. Механизмы контактной информации (БСП): в типовых конфигурациях 1С для адресов и телефонов используется готовая подсистема контактной информации, позволяющая задавать неограниченное число адресов с привязкой к видам контактной информации и классификаторам.
← На главную