Генерация уникального идентификатора в 1С 8.3: от одной строки кода до обмена между базами
Содержание
Новый уникальный идентификатор в 1С 8.3 создаётся одной строкой: УИД = Новый УникальныйИдентификатор;. Чтобы получить его в виде текста, оберните в Строка(): получится строка из 36 символов вида 3f2a9c1e-7b4d-4e8a-9c21-5d0f6e7a8b90. Конструктор работает на клиенте и на сервере, в любой конфигурации — БП, ЗУП, УТ, ERP, — потому что это механизм платформы, а не прикладного решения.
Сложности начинаются дальше: как взять GUID у существующего документа, как найти объект по строке из внешней системы, как записать элемент с заранее известным идентификатором и почему GUID в SQL-таблице не совпадает с тем, что показывает 1С. Под каждую из этих задач есть свой приём и готовый код.
Что такое GUID и где он нужен в 1С
GUID (он же UUID) — 128-битное число, записанное как 32 шестнадцатеричные цифры в пяти группах: 8-4-4-4-12. Вероятность того, что два случайно сгенерированных GUID совпадут, настолько мала, что на практике ей пренебрегают.
В 1С у каждого объекта ссылочного типа — элемента справочника, документа, плана видов характеристик, бизнес-процесса — есть собственный GUID. Именно он лежит внутри ссылки. Наименование и код можно поменять, а идентификатор остаётся тем же всю жизнь объекта.
Где разработчику и администратору нужен GUID:
- 🔄 обмен данными — сопоставление объектов между базами и с внешними системами (сайт, маркетплейс, WMS);
- 🌐 HTTP-сервисы и REST — передача ключа объекта во внешний мир вместо кода, который может повториться;
- 🗂 временное хранилище — адрес привязывают к уникальному идентификатору формы;
- 📝 реквизиты-ключи — хранение внешнего идентификатора в реквизите типа
УникальныйИдентификатор; - 🧾 логи и очереди — метка сообщения или пакета, чтобы не обработать его дважды.
Какой способ выбрать — зависит от задачи:
| Задача | Способ | Где работает |
|---|---|---|
| Получить новый случайный GUID | Новый УникальныйИдентификатор | клиент и сервер, любая ОС |
| Узнать GUID существующего объекта | Ссылка.УникальныйИдентификатор() | клиент и сервер |
| Найти объект по строке GUID | Менеджер.ПолучитьСсылку(УИД) | сервер |
| Записать объект с заданным GUID | УстановитьСсылкуНового() | сервер |
| GUID из системного генератора Windows | COM-объект | только Windows |
| Нестандартный формат (версия 1, 7 и др.) | внешняя компонента | по возможностям компоненты |
Способ 1. Создать новый уникальный идентификатор встроенной функцией
Основной и почти всегда достаточный способ. Конструктор без параметров генерирует случайный идентификатор:
УИД = Новый УникальныйИдентификатор;
СтрокаУИД = Строка(УИД);
Сообщить(СтрокаУИД); // например: 9b1f4c2a-0d3e-4b7f-8a61-2c5e9d0f1a34
Тип результата — УникальныйИдентификатор, а не строка. Это важно: такое значение можно сравнивать через =, хранить в реквизите соответствующего типа и передавать в ПолучитьСсылку().
Создание из готовой строки
Если GUID пришёл извне текстом, конструктору передают строку:
СтрокаИзвне = "9b1f4c2a-0d3e-4b7f-8a61-2c5e9d0f1a34";
УИД = Новый УникальныйИдентификатор(СтрокаИзвне);
Строка должна быть строго в формате 8-4-4-4-12. Фигурные скобки, лишние пробелы или 32 цифры без дефисов дадут ошибку выполнения. Поэтому внешние данные сначала чистят, а конструктор оборачивают в Попытка:
Функция УИДИзСтроки(Знач Текст) Экспорт
Текст = СокрЛП(Текст);
Текст = СтрЗаменить(Текст, "{", "");
Текст = СтрЗаменить(Текст, "}", "");
Попытка
Возврат Новый УникальныйИдентификатор(Текст);
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
Функция возвращает Неопределено, если строка не похожа на GUID. Вызывающий код проверяет результат и решает, что делать: пропустить запись или записать её в журнал ошибок.
Пример: GUID формы для временного хранилища
Типичный случай из практики — загрузка файла на сервер. Адрес хранилища привязывают к идентификатору формы, чтобы данные удалились вместе с её закрытием:
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
Адрес = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
// дальше — НачатьПомещениеФайла или ПоместитьФайл с этим адресом
КонецПроцедуры
Здесь УникальныйИдентификатор — свойство самой управляемой формы. Новый GUID генерировать не нужно.
Способ 2. GUID через COM-объект Windows
До появления удобного конструктора разработчики брали идентификатор у системного генератора Windows. Способ встречается в старом коде, поэтому полезно его узнавать:
Генератор = Новый COMОбъект("Scriptlet.TypeLib");
СыройGUID = Генератор.Guid;
СтрокаУИД = НРег(Сред(СыройGUID, 2, 36));
Guid возвращает строку в фигурных скобках и заглавными буквами, иногда с невидимыми символами в конце. Поэтому из неё вырезают 36 символов, начиная со второго.
Ограничения способа:
- 🪟 работает только на Windows: на сервере 1С под Linux и в веб-клиенте COM недоступен;
- 🔒 в части сборок Windows объект
Scriptlet.TypeLibотключён политиками безопасности, и код падает с ошибкой создания COM-объекта; - 🐢 создание COM-объекта заметно медленнее встроенного конструктора.
В новом коде COM для генерации GUID не нужен. Если встретили его в доработке, замените на Новый УникальныйИдентификатор — поведение для программы то же, а зависимость от ОС уходит.
Способ 3. Внешняя компонента для особых форматов
Встроенный генератор выдаёт случайный идентификатор. Для большинства задач этого хватает. Внешняя компонента нужна, когда внешняя система требует GUID другой версии стандарта:
- ⏱ версия 1 — содержит метку времени и признак узла;
- 📈 версия 7 — начинается с метки времени, поэтому такие GUID сортируются по порядку создания. Это удобно для индексов внешних СУБД и очередей сообщений;
- 🔁 версия 5 — детерминированный: из одной и той же строки всегда получается один и тот же GUID.
Компоненту по технологии Native API подключают через ПодключитьВнешнююКомпоненту() (или асинхронный аналог на клиенте) и создают её объект через Новый("AddIn.ИмяКомпоненты.ИмяОбъекта"). Имена методов задаёт автор компоненты, общего стандарта нет — смотрите её описание.
Альтернатива без компоненты — вызвать системную утилиту. На Linux есть uuidgen, на Windows — PowerShell-команда [guid]::NewGuid(). Запуск идёт через ЗапуститьПриложение() с выводом в файл, который потом читают. Способ медленный и годится для разовых операций, а не для цикла по тысячам строк.
Версию 5 можно получить и без компоненты, если реализовать алгоритм на хеше SHA-1 в коде 1С. Но это уже собственная разработка с тестами на эталонных значениях из RFC 4122 — без проверки легко получить «почти GUID», который внешняя система отвергнет.
Способ 4. Уникальный идентификатор в запросах 1С
Язык запросов 1С не умеет генерировать GUID: функции вроде NEWID() из SQL Server в нём нет. Зато с готовыми идентификаторами запрос работает нормально.
Отбор по GUID ссылки
Ссылку собирают в коде и передают параметром:
УИД = Новый УникальныйИдентификатор("9b1f4c2a-0d3e-4b7f-8a61-2c5e9d0f1a34");
Ссылка = Справочники.Номенклатура.ПолучитьСсылку(УИД);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование,
| Номенклатура.Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Результат = Запрос.Выполнить();
Пустой результат значит, что объекта с таким идентификатором в базе нет.
Отбор по реквизиту типа «УникальныйИдентификатор»
Если внешний ключ хранится в отдельном реквизите (назовём его ВнешнийИД), параметр передают значением типа УникальныйИдентификатор, а не строкой:
Запрос.УстановитьПараметр("ВнешнийИД", Новый УникальныйИдентификатор(СтрокаИзвне));
Сравнение строки с реквизитом этого типа не найдёт ничего.
Массовая выдача новых GUID
Когда новый идентификатор нужен каждой строке выборки, его генерируют в цикле после запроса:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НовыйУИД = Новый УникальныйИдентификатор;
// записываем НовыйУИД в реквизит или в таблицу значений
КонецЦикла;
Генерация в коде быстрая: десятки тысяч идентификаторов создаются за доли секунды, узким местом будет запись в базу, а не конструктор.
Список GUID как параметр запроса
Если из внешней системы пришёл список идентификаторов, не делайте запрос на каждый. Соберите массив ссылок и отберите всё одним запросом через В (&Ссылки):
Ссылки = Новый Массив;
Для Каждого СтрокаУИД Из СписокИзвне Цикл
УИД = УИДИзСтроки(СтрокаУИД);
Если УИД <> Неопределено Тогда
Ссылки.Добавить(Справочники.Номенклатура.ПолучитьСсылку(УИД));
КонецЕсли;
КонецЦикла;
Запрос.УстановитьПараметр("Ссылки", Ссылки);
Ссылки, которых нет в выборке, — это объекты, отсутствующие в базе. Их удобно сразу вывести в протокол загрузки.
Способ 5. Получить GUID существующего объекта
Чаще всего «создать GUID» на деле значит «достать его у объекта». У любой ссылки есть метод УникальныйИдентификатор():
Ссылка = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("0000-000123", ТекущаяДата());
Если Не Ссылка.Пустая() Тогда
СтрокаУИД = Строка(Ссылка.УникальныйИдентификатор());
КонецЕсли;
У пустой ссылки идентификатор состоит из нулей: 00000000-0000-0000-0000-000000000000. Поэтому перед выгрузкой проверяйте Ссылка.Пустая(), иначе во внешнюю систему уйдёт «нулевой» ключ.
Обратная операция: ссылка по GUID
УИД = Новый УникальныйИдентификатор(СтрокаУИД);
Ссылка = Справочники.Контрагенты.ПолучитьСсылку(УИД);
Если Ссылка.ПолучитьОбъект() = Неопределено Тогда
// объекта с таким GUID нет — ссылка «битая»
КонецЕсли;
ПолучитьСсылку() не проверяет существование объекта. Она всегда возвращает ссылку, даже на несуществующий элемент. В интерфейсе такая ссылка отображается как <Объект не найден> с идентификатором. Проверку делают через ПолучитьОбъект() или запросом. Для массовой проверки запрос быстрее: он не читает объект целиком.
Новый объект с заранее заданным GUID
Так переносят данные между базами с сохранением идентификаторов, чтобы ссылки в документах «склеились»:
УИД = Новый УникальныйИдентификатор(СтрокаИзИсточника);
Ссылка = Справочники.Номенклатура.ПолучитьСсылку(УИД);
Если Ссылка.ПолучитьОбъект() = Неопределено Тогда
Объект = Справочники.Номенклатура.СоздатьЭлемент();
Объект.УстановитьСсылкуНового(Ссылка);
Объект.Наименование = "Товар из источника";
Объект.Записать();
КонецЕсли;
Метод УстановитьСсылкуНового() вызывают до первой записи. У уже записанного объекта GUID сменить нельзя.
Обратный приём — узнать будущую ссылку нового объекта до записи. Его используют, например, чтобы привязать присоединённые файлы к ещё не записанному документу. Схема такая: создать ссылку из нового GUID, назначить её через УстановитьСсылкуНового(), а дальше использовать в связанных данных. Метод ПолучитьСсылкуНового() возвращает ссылку, назначенную таким способом; если её не назначали — пустую ссылку.
GUID в SQL и навигационной ссылке
Администраторы, которые смотрят таблицы в SQL Server или PostgreSQL, видят идентификатор в поле _IDRRef, и он не совпадает со строкой из 1С. Байты там переставлены: группы идут в порядке 4-5-3-2-1.
| Где | Как выглядит |
|---|---|
1С, Строка(УИД) | aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee |
SQL, _IDRRef | 0xDDDDEEEEEEEEEEEECCCCBBBBAAAAAAAA |
То же переставленное шестнадцатеричное представление стоит в навигационной ссылке (e1cib/data/...), которую возвращает ПолучитьНавигационнуюСсылку().
Не меняйте _IDRRef прямыми SQL-запросами в рабочей базе — ссылки в табличных частях и регистрах останутся старыми, и данные рассыплются.
Типичные ошибки с GUID и как их избежать
Ещё одна тонкость — регистр букв. 1С выдаёт GUID строчными буквами, Windows и многие внешние API — заглавными. Конструктор Новый УникальныйИдентификатор() принимает оба варианта, а вот прямое сравнение строк "ABC..." = "abc..." даст Ложь. Сравнивайте значения типа УникальныйИдентификатор либо приводите строки через НРег().
Частые вопросы
Может ли 1С сгенерировать два одинаковых GUID?
Сохраняется ли GUID при выгрузке и загрузке базы через .dt?
Меняется ли GUID при пометке на удаление или перепроведении?
Можно ли сделать реквизит типа «УникальныйИдентификатор» в конфигураторе?
Есть ли GUID у строк регистров и табличных частей?
УникальныйИдентификатор и заполняйте его при создании строки.