Как исправить ошибку «Попытка вставки записи с неуникальным значением ссылки» при обмене данными в 1С?

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

При выполнении обмена данными между конфигурациями 1С через универсальную обработку ОбменДаннымиXML (технология «Конвертация данных 2.1») разработчики и администраторы нередко сталкиваются с аварийным завершением загрузки. В протоколе обмена фиксируется ошибка: «Ошибка при вызове метода контекста (Записать): Ошибка при попытке вставки записи с неуникальным значением ссылки. таблица Reference...».

Разберем детально механику возникновения этой ошибки, выясним, почему попытка просто «заглушить» исключение в коде обработки приводит к тяжелым последствиям для базы данных, и рассмотрим по шагам корректные способы диагностики и устранения сбоя.

Механизм возникновения проблемы и почему возникает ошибка СУБД

Проанализируем, что происходит внутри платформы 1С:Предприятие при чтении данных из файла обмена:

  1. Обработка считывает из входящего XML-файла элемент (в нашем случае это справочник НоменклатурныеГруппы) и его уникальный идентификатор (GUID).
  2. На основе настроенных в правилах конвертации объектов (ПКО) полей поиска выполняется попытка найти существующий элемент в базе-приемнике (например, по реквизитам Код и Наименование).
  3. Если по указанным реквизитам объект не найден, алгоритм загрузки считает, что в базе-приемнике такого элемента еще нет, и вызывает конструктор СоздатьЭлемент().
  4. Для сохранения сквозной ссылочной целостности между базами платформенным методом УстановитьСсылкуНового() объекту явно назначается УИД, прочитанный из файла источника.
  5. При последующем выполнении команды Объект.Записать() платформа пытается сформировать к СУБД SQL-запрос INSERT с данным идентификатором. Однако на уровне базы данных запись с таким первичным ключом уже физически присутствует в таблице справочника (в сообщении об ошибке это таблица вида Reference130). СУБД возвращает ошибку нарушения уникальности первичного ключа (PRIMARY KEY).

Таким образом, объект с таким идентификатором в базе уже существует, но поиск в правилах конвертации не смог сопоставить входящие данные с имеющимся объектом (например, из-за изменения наименования или переноса в другую группу).

Почему нельзя просто заглушать ошибку через «Попытка — Исключение»?

Посмотрим на попытку обойти проблему, предложенную в обсуждении: перехватить ошибку в процедуре ЗаписатьОбъектВИБ обработки ОбменДаннымиXML и проигнорировать запись объекта. Выясним, к чему это приведет:

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

Следовательно, решать проблему нужно не блокировкой исключения, а устранением первопричины конфликта идентификаторов.

Шаг 1. Точная диагностика таблицы и конфликтного элемента

В первую очередь выясним, к какому именно метаданному относится таблица, указанная в тексте ошибки (например, Reference130), так как номера таблиц формируются СУБД динамически при реструктуризации. Для этого воспользуемся встроенной функцией:


СтруктураБД = ПолучитьСтруктуруХраненияБазыДанных();
Для Каждого СтрокаТаблицы Из СтруктураБД Цикл
    Если СтрокаТаблицы.ИмяТаблицыХранения = "_Reference130" Тогда
        Сообщить("Таблице соответствует объект: " + СтрокаТаблицы.Имя);
    КонецЕсли;
КонецЦикла;

Убедившись, что речь идет о справочнике НоменклатурныеГруппы, найдем идентификатор проблемного элемента в базе-источнике:


// Выполняем в базе-источнике:
ЭлементИсточника = Справочники.НоменклатурныеГруппы.НайтиПоНаименованию("Блоки бетонные стен подвалов");
Если ЗначениеЗаполнено(ЭлементИсточника) Тогда
    Сообщить("GUID источника: " + Строка(ЭлементИсточника.УникальныйИдентификатор()));
КонецЕсли;

Теперь проанализируем состояние этого же идентификатора в базе-приемнике:


// Выполняем в базе-приемнике:
СтрокаGUID = "укажите-полученный-guid-здесь";
УИД = Новый УникальныйИдентификатор(СтрокаGUID);
СсылкаПриемника = Справочники.НоменклатурныеГруппы.ПолучитьСсылку(УИД);

ОбъектПриемника = СсылкаПриемника.ПолучитьОбъект();
Если ОбъектПриемника <> Неопределено Тогда
    Сообщить("Элемент в приемнике найден: " + ОбъектПриемника.Наименование + ", Код: " + ОбъектПриемника.Код);
Иначе
    Сообщить("Объект по ссылке не получен (возможно, повреждение индекса или элемент помечен на удаление)");
КонецЕсли;

Сравнив значения реквизитов Код, Наименование или Родитель у элементов в обеих базах, мы сразу увидим расхождение, из-за которого типовой алгоритм поиска дал сбой.

Шаг 2. Способы исправления ошибки

Рассмотрим возможные варианты устранения конфликта в зависимости от используемой архитектуры обмена.

Вариант 1. Корректировка правил конвертации (Конвертация данных 2.1)

Это наиболее правильный и системный способ решения проблемы. Разберем действия в конфигурации «Конвертация данных»:

  1. Откроем правило конвертации объекта (ПКО) для справочника НоменклатурныеГруппы.
  2. Установим флаг «Искать объект приемника по внутреннему идентификатору». В этом случае платформа сначала выполнит сопоставление по GUID. Обнаружив существующую запись, она выполнит операцию UPDATE (обновление реквизитов), а не попытку создания дублирующего объекта через INSERT.
  3. Если сопоставление обязательно должно выполняться только по реквизитам (например, наименованию), то в обработчике события ПоляПоиска необходимо переопределить генерацию ссылки, чтобы при отсутствии совпадений по реквизитам новому объекту не навязывался GUID источника:

// Обработчик "Поля поиска" в ПКО
Если НомерВариантаПоиска = 1 Тогда
    // Если по реквизитам объект не найден, отменяем привязку старого УИД:
    ПрекратитьПоиск = Ложь;
КонецЕсли;

Вариант 2. Ручная синхронизация данных в базе-приемнике

Если редактирование правил конвертации нежелательно или обмен типовой, приведем данные к единообразию вручную:

  1. В базе-приемнике находим конфликтный элемент (вычисленный нами по GUID на первом шаге).
  2. Меняем его реквизиты (код, наименование, группу) в точном соответствии с базой-источником, чтобы стандартный алгоритм поиска успешно нашел его в момент загрузки.
  3. Если выяснилось, что по указанному GUID в базе-приемнике находится ошибочно созданный или лишний элемент, на который нет ссылок в базе, помечаем его на удаление и удаляем стандартной процедурой Удаление помеченных объектов.
  4. Если на конфликтный элемент уже завязаны документы в базе-приемнике, используем типовую обработку Поиск и удаление дублей, перенаправив ссылки на корректный элемент.

Вариант 3. Проверка прав доступа (RLS)

Проанализируем еще одну частую причину ошибки: загрузка данных под пользователем с ограниченными правами. Если для справочника настроен механизм RLS (ограничение доступа на уровне записей), то существующий элемент может быть банально невидим для запроса поиска. Обработка загрузки делает ошибочный вывод, что объект отсутствует, и пытается выполнить создание новой записи с тем же GUID.

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

← На главную