В процессе разработки и сопровождения систем на платформе 1С:Предприятие 8.3 часто возникает необходимость синхронизации данных между разными информационными базами. Одной из сложнейших задач в этом контексте является ситуация, когда один и тот же объект в разных базах имеет разные уникальные идентификаторы (GUID). Поскольку УникальныйИдентификатор является первичным ключом в базе данных, его прямое изменение у уже существующего объекта невозможно стандартными средствами встроенного языка. Однако существуют обходные пути, которые позволяют достичь нужного результата.
В этой статье мы подробно рассмотрим, почему нельзя просто «перезаписать» идентификатор, разберем алгоритм замены идентификатора через создание нового объекта, проанализируем методы поиска «битых» ссылок и изучим использование инструментов БСП для этой задачи. Для этой задачи есть обработка поиска и удаления битых ссылок без монопольного режима.
Прежде чем переходить к практике, разберем техническую причину ограничения. На уровне СУБД (будь то Microsoft SQL Server, PostgreSQL или другие) уникальный идентификатор хранится в поле _IDRRef. Это поле является частью кластерного индекса и первичным ключом таблицы. Платформа 1С при вызове метода Записать() для существующего объекта генерирует SQL-запрос UPDATE, где в условии WHERE используется текущая ссылка. Если мы программно попытаемся подменить ссылку в памяти у объекта, который уже «знает» свой старый идентификатор, система не сможет корректно сопоставить данные в базе и в оперативной памяти.
Таким образом, единственно верный путь — это создание «двойника» объекта с правильным идентификатором и последующая замена всех ссылок на него во всей информационной базе.
Рассмотрим ситуацию, когда нам нужно программно создать элемент справочника, точно зная, какой GUID он должен иметь (например, полученный из другой базы через COM-соединение или XML). Для этого используется метод УстановитьСсылкуНового(). Разберем по шагам, как это реализовать (а если нужно просто найти объект по его GUID в интерактивном режиме, можно воспользоваться готовыми инструментами):
УникальныйИдентификатор из строкового представления.УстановитьСсылкуНового() до первой записи объекта.Посмотрим на пример кода:
// Допустим, мы получили GUIDСтрока из внешней системы
GUIDОбъект = Новый УникальныйИдентификатор(GUIDСтрока);
НоваяСсылка = Справочники.Номенклатура.ПолучитьСсылку(GUIDОбъект);
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
// Важно: устанавливаем ссылку до заполнения и записи
НовыйЭлемент.УстановитьСсылкуНового(НоваяСсылка);
НовыйЭлемент.Наименование = "Тестовый товар";
// Заполняем остальные реквизиты...
НовыйЭлемент.Записать();
Важный нюанс: метод УстановитьСсылкуНового() работает только для объектов, которые еще ни разу не были записаны в базу данных. Если вы попытаетесь вызвать его для уже существующего объекта (ВыбраннаяСсылка.ПолучитьОбъект()), платформа проигнорирует этот вызов.
Если элемент со «старым» идентификатором уже существует и на него есть ссылки в документах или регистрах, алгоритм усложняется. Рассмотрим последовательность действий:
GUID.Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), эту задачу можно решить изящнее с помощью функции ОбщегоНазначения.ЗаменитьСсылки(). Как альтернативу для таких операций можно использовать универсальные редакторы данных — для этого подойдет набор инструментов разработчика для поиска и редактирования объектов 1С. Проанализируем пример процедуры с использованием БСП:
&НаСервереБезКонтекста
Процедура ЗаменитьИдентификаторОбъекта(СтараяСсылка, НовыйGUIDСтрока)
// 1. Создаем новый объект с правильным GUID
НовыйGUID = Новый УникальныйИдентификатор(НовыйGUIDСтрока);
НоваяСсылка = Справочники.Номенклатура.ПолучитьСсылку(НовыйGUID);
// Копируем данные
ОбъектОригинал = СтараяСсылка.ПолучитьОбъект();
НовыйОбъект = ОбъектОригинал.Скопировать();
НовыйОбъект.УстановитьСсылкуНового(НоваяСсылка);
// Отключаем проверки, если это необходимо для загрузки данных
НовыйОбъект.ОбменДанными.Загрузка = Истина;
НовыйОбъект.Записать();
// 2. Подготовка таблицы замен для БСП
ПарыЗамен = Новый Соответствие;
ПарыЗамен.Вставить(СтараяСсылка, НоваяСсылка);
// 3. Вызов функции замены ссылок
// Параметр "СпособУдаления" = "Непосредственно" удалит старый объект сам
ПараметрыЗамены = Новый Структура;
ПараметрыЗамены.Вставить("СпособУдаления", "Непосредственно");
ПараметрыЗамены.Вставить("ЗаменаПарыВТранзакции", Истина);
ОбщегоНазначения.ЗаменитьСсылки(ПарыЗамен, ПараметрыЗамены);
КонецПроцедуры
Существует «ленивый», но рабочий способ, который не требует написания сложного кода по переносу всех реквизитов вручную. Это выгрузка и загрузка через XML — для этого отлично подойдет универсальный инструмент переноса данных между базами 1С. Выясним причину популярности этого метода: при чтении объекта из XML-файла платформа 1С позволяет явно указать идентификатор в заголовке объекта.
Рассмотрим шаги этого процесса:
GUID, и заменяем его на новый.При работе с идентификаторами, особенно при интеграции через BasePriemnik.Справочники.Имя.ПолучитьСсылку(GUID), часто возникает вопрос: существует ли реально объект с таким идентификатором в базе или мы получили «пустую» ссылку (объект не найден)? Последствия таких ситуаций могут приводить к необходимости удаления движений по битым ссылкам.
Рассмотрим и сравним разные подходы к проверке:
ПолучитьОбъект(): Самый медленный способ. Если ссылка «битая», метод вернет Неопределено. Однако платформа при этом пытается считать все поля записи, что создает излишнюю нагрузку на сервер и сеть.СтрНайти(Ссылка, "Объект не найден"): Часто предлагаемый, но крайне небезопасный способ. Результат зависит от языка локализации платформы. В английской версии это будет "Object not found", в русской — "Объект не найден", а в других языках — иные варианты. Никогда не используйте этот метод в тиражируемых решениях.Посмотрим на пример функции для быстрой проверки существования ссылки:
Функция СсылкаСуществует(ПроверяемаяСсылка) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Ссылка КАК Ссылка
|ИЗ
| " + ПроверяемаяСсылка.Метаданные().ПолноеИмя() + " КАК Т
|ГДЕ
| Т.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ПроверяемаяСсылка);
Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции
Если в вашей конфигурации есть БСП, рекомендуется использовать готовую функцию ОбщегоНазначения.СсылкаСуществует(), которая реализует аналогичную логику оптимальным образом.
Если вы меняете или ищете идентификаторы, подключаясь к другой базе программно, помните о типах данных. УникальныйИдентификатор в вашей базе и УникальныйИдентификатор в базе COM — это разные типы объектов с точки зрения системы. Для корректного поиска всегда приводите идентификатор к строке и обратно.
Проанализируем пример кода для поиска в базе-приемнике:
// В базе-источнике получаем строку GUID
СтрGUID = Строка(ЭлементИсточник.Ссылка.УникальныйИдентификатор());
// В базе-приемнике (через COM) преобразуем строку обратно в тип GUID приемника
RemoteGUID = BasePriemnik.NewObject("УникальныйИдентификатор", СтрGUID);
СсылкаПриемник = BasePriemnik.Справочники.Номенклатура.GetRef(RemoteGUID);
// Проверяем существование в приемнике
Если СсылкаПриемник.IsEmpty() Тогда
// Создаем новый с этим GUID
КонецЕсли;
Завершая разбор, выделим критически важные моменты, которые следует учитывать при замене идентификаторов:
ЗаменитьСсылки() в периодических регистрах сведений могут возникнуть коллизии (дублирование ключей записей). Всегда проверяйте результаты в таких регистрах после завершения операции.GUID в одной из баз РИБ, помните, что изменения должны «пролететь» по всем узлам. Создание нового объекта и удаление старого — более безопасный путь для РИБ, чем любые манипуляции на уровне СУБД._IDRRef напрямую через SQL-запросы в рабочей базе. Это приведет к немедленному появлению «битых» ссылок во всех связанных таблицах, так как 1С не поддерживает каскадное обновление на уровне СУБД. Кроме того, кэш сервера приложений 1С «запомнит» старое состояние объекта, что вызовет непредсказуемые ошибки.Таким образом, хотя платформа 1С не позволяет «просто изменить» GUID, комбинация методов УстановитьСсылкуНового() и механизмов замены ссылок позволяет решить задачу любой сложности, сохраняя целостность данных и стабильность работы системы.