При выполнении обмена данными между конфигурациями 1С через универсальную обработку ОбменДаннымиXML (технология «Конвертация данных 2.1») разработчики и администраторы нередко сталкиваются с аварийным завершением загрузки. В протоколе обмена фиксируется ошибка: «Ошибка при вызове метода контекста (Записать): Ошибка при попытке вставки записи с неуникальным значением ссылки. таблица Reference...».
Разберем детально механику возникновения этой ошибки, выясним, почему попытка просто «заглушить» исключение в коде обработки приводит к тяжелым последствиям для базы данных, и рассмотрим по шагам корректные способы диагностики и устранения сбоя.
Проанализируем, что происходит внутри платформы 1С:Предприятие при чтении данных из файла обмена:
НоменклатурныеГруппы) и его уникальный идентификатор (GUID).Код и Наименование).СоздатьЭлемент().УстановитьСсылкуНового() объекту явно назначается УИД, прочитанный из файла источника.Объект.Записать() платформа пытается сформировать к СУБД SQL-запрос INSERT с данным идентификатором. Однако на уровне базы данных запись с таким первичным ключом уже физически присутствует в таблице справочника (в сообщении об ошибке это таблица вида Reference130). СУБД возвращает ошибку нарушения уникальности первичного ключа (PRIMARY KEY).Таким образом, объект с таким идентификатором в базе уже существует, но поиск в правилах конвертации не смог сопоставить входящие данные с имеющимся объектом (например, из-за изменения наименования или переноса в другую группу).
Посмотрим на попытку обойти проблему, предложенную в обсуждении: перехватить ошибку в процедуре ЗаписатьОбъектВИБ обработки ОбменДаннымиXML и проигнорировать запись объекта. Выясним, к чему это приведет:
Исключение во встроенном языке, последующий вызов метода ЗафиксироватьТранзакцию() завершится критической ошибкой платформы.Следовательно, решать проблему нужно не блокировкой исключения, а устранением первопричины конфликта идентификаторов.
В первую очередь выясним, к какому именно метаданному относится таблица, указанная в тексте ошибки (например, Reference130), так как номера таблиц формируются СУБД динамически при реструктуризации. Для этого воспользуемся встроенной функцией:
СтруктураБД = ПолучитьСтруктуруХраненияБазыДанных();
Для Каждого СтрокаТаблицы Из СтруктураБД Цикл
Если СтрокаТаблицы.ИмяТаблицыХранения = "_Reference130" Тогда
Сообщить("Таблице соответствует объект: " + СтрокаТаблицы.Имя);
КонецЕсли;
КонецЦикла;
Убедившись, что речь идет о справочнике НоменклатурныеГруппы, найдем идентификатор проблемного элемента в базе-источнике:
// Выполняем в базе-источнике:
ЭлементИсточника = Справочники.НоменклатурныеГруппы.НайтиПоНаименованию("Блоки бетонные стен подвалов");
Если ЗначениеЗаполнено(ЭлементИсточника) Тогда
Сообщить("GUID источника: " + Строка(ЭлементИсточника.УникальныйИдентификатор()));
КонецЕсли;
Теперь проанализируем состояние этого же идентификатора в базе-приемнике:
// Выполняем в базе-приемнике:
СтрокаGUID = "укажите-полученный-guid-здесь";
УИД = Новый УникальныйИдентификатор(СтрокаGUID);
СсылкаПриемника = Справочники.НоменклатурныеГруппы.ПолучитьСсылку(УИД);
ОбъектПриемника = СсылкаПриемника.ПолучитьОбъект();
Если ОбъектПриемника <> Неопределено Тогда
Сообщить("Элемент в приемнике найден: " + ОбъектПриемника.Наименование + ", Код: " + ОбъектПриемника.Код);
Иначе
Сообщить("Объект по ссылке не получен (возможно, повреждение индекса или элемент помечен на удаление)");
КонецЕсли;
Сравнив значения реквизитов Код, Наименование или Родитель у элементов в обеих базах, мы сразу увидим расхождение, из-за которого типовой алгоритм поиска дал сбой.
Рассмотрим возможные варианты устранения конфликта в зависимости от используемой архитектуры обмена.
Это наиболее правильный и системный способ решения проблемы. Разберем действия в конфигурации «Конвертация данных»:
НоменклатурныеГруппы.UPDATE (обновление реквизитов), а не попытку создания дублирующего объекта через INSERT.ПоляПоиска необходимо переопределить генерацию ссылки, чтобы при отсутствии совпадений по реквизитам новому объекту не навязывался GUID источника:
// Обработчик "Поля поиска" в ПКО
Если НомерВариантаПоиска = 1 Тогда
// Если по реквизитам объект не найден, отменяем привязку старого УИД:
ПрекратитьПоиск = Ложь;
КонецЕсли;
Если редактирование правил конвертации нежелательно или обмен типовой, приведем данные к единообразию вручную:
Проанализируем еще одну частую причину ошибки: загрузка данных под пользователем с ограниченными правами. Если для справочника настроен механизм RLS (ограничение доступа на уровне записей), то существующий элемент может быть банально невидим для запроса поиска. Обработка загрузки делает ошибочный вывод, что объект отсутствует, и пытается выполнить создание новой записи с тем же GUID.
Для предотвращения подобных коллизий всегда выполняйте процедуру обмена от имени пользователя с полными административными правами либо обеспечьте запуск процедур загрузки в привилегированном режиме.