В практике разработчика 1С нередко возникает ситуация, когда на руках имеется лишь список уникальных идентификаторов (УИД), и необходимо понять, к каким именно данным они относятся. Например, это может быть Справочник.Номенклатура, Документ.РеализацияТоваровУслуг или любой другой объект метаданных. В этой статье мы подробно разберем, как устроены идентификаторы в системе, почему невозможно узнать тип объекта напрямую из набора символов и какие существуют стратегии решения этой задачи. Для быстрого интерактивного решения этой задачи можно применить универсальную обработку для взаимного поиска ссылок и их УИД.
Для начала проанализируем техническую природу идентификатора. В платформе 1С:Предприятие используется стандарт RFC 4122 для генерации уникальных идентификаторов. Чаще всего это 128-битное число, которое в строковом виде выглядит как 3680327e-324c-11e1-80bb-0015e92c2102. Рассмотрим ключевые причины отсутствия метаданных в этой строке:
_IDRRef в бинарном формате binary(16). Сама таблица "знает", что в ней лежат только ссылки на Номенклатуру, потому что это прописано в структуре конфигурации, а не в самом значении идентификатора.Если у нас есть УИД и нам нужно найти его владельца средствами встроенного языка, мы можем пойти путем последовательного опроса метаданных. Выясним, как это реализовать оптимально. Основная идея заключается в использовании метода ПолучитьСсылку() у менеджеров объектов.
Рассмотрим пример кода, который обходит справочники и пытается найти объект:
Функция ОпределитьТипПоУИД(СтрУИД)
УИ = Новый УникальныйИдентификатор(СтрУИД);
// Обходим все справочники
Для Каждого МетаСправочник Из Метаданные.Справочники Цикл
Менеджер = Справочники[МетаСправочник.Имя];
Ссылка = Менеджер.ПолучитьСсылку(УИ);
// Проверяем, существует ли объект в базе
Если НайтиОбъектПоСсылке(Ссылка) Тогда
Возврат Ссылка;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Функция НайтиОбъектПоСсылке(Ссылка)
// Метод ПолучитьОбъект() или запрос - наиболее надежный способ проверки
// Но помним, что ПолучитьОбъект() тянет все данные, это затратно.
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ " + Ссылка.Метаданные().ПолноеИмя() + " ГДЕ Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции
Важный нюанс: Обратите внимание, что простое получение ссылки через ПолучитьСсылку(УИ) не обращается к базе данных. Система "соберет" ссылку в памяти, даже если объекта с таким УИД не существует. Поэтому проверка через ПолучитьОбъект() или запрос ВЫБРАТЬ Ссылка обязательна.
Если идентификаторов много, выполнение запроса в цикле для каждого типа метаданных будет крайне медленным. Проанализируем более эффективный подход — использование оператора ОБЪЕДИНИТЬ ВСЕ. Мы можем сформировать один большой запрос, который проверит наличие ссылок сразу в нескольких вероятных таблицах. Для автоматизации этого процесса при работе с внешними файлами подойдёт инструмент для массового поиска объектов по списку уникальных идентификаторов.
Посмотрим на пример формирования такого запроса:
ТекстЗапроса = "";
Для Каждого ТипОбъекта Из СписокТипов Цикл
Если Не ПустаяСтрока(ТекстЗапроса) Тогда
ТекстЗапроса = ТекстЗапроса + " ОБЪЕДИНИТЬ ВСЕ ";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса +
"ВЫБРАТЬ Ссылка, """ + ТипОбъекта + """ КАК ИмяТипа
|ИЗ " + ТипОбъекта + " ГДЕ Ссылка В (&МассивСсылок)";
КонецЦикла;
Такой подход сокращает количество обращений к СУБД. Однако помните, что количество соединений в одном запросе ограничено возможностями SQL-сервера, поэтому при очень большом количестве метаданных (сотни справочников и документов) запрос стоит разбивать на пачки.
Это, пожалуй, самый быстрый и современный способ найти объект, зная только строковое представление его УИД. Если в вашей базе данных включен и обновлен индекс полнотекстового поиска, система может мгновенно найти ссылку.
Алгоритм действий:
ПолнотекстовыйПоиск.СоздатьСписок("Тут_Ваш_УИД").ПерваяПорция().Результат[0].Значение.ТипЗнч(Ссылка).Этот метод хорош тем, что он не перебирает таблицы по одной, а использует заранее подготовленный индекс. Ограничение: объект должен быть включен в полнотекстовый поиск в свойствах метаданных.
В новых релизах платформы (начиная с 8.3.21) работа с УИД в запросах стала значительно проще. Теперь мы можем использовать функцию УникальныйИдентификатор(Ссылка) прямо в тексте запроса. Это позволяет эффективно сопоставлять данные из внешних источников с объектами базы.
Рассмотрим пример сопоставления списка УИД из внешней таблицы значений с реальными справочниками, для ситуаций же, когда идентификаторы объектов в базах различаются и требуется сопоставление по произвольным реквизитам, используется перенос данных между похожими конфигурациями 1С:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ВнешниеДанные.УИД КАК УИД
|ПОМЕСТИТЬ ВТ_УИДы
|ИЗ
| &ТЗ_ВнешниеДанные КАК ВТ_ВнешниеДанные
|;
|
|ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| ВТ_УИДы.УИД КАК ИсходныйУИД
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_УИДы КАК ВТ_УИДы
| ПО УникальныйИдентификатор(Номенклатура.Ссылка) = ВТ_УИДы.УИД";
Такая конструкция работает на уровне СУБД, что обеспечивает высокую производительность и исключает необходимость преобразования ссылок в строки на стороне сервера 1С.
Если вы решите искать УИД напрямую в SQL-базе (например, через системные вьюхи sys.columns), важно помнить о "проблеме перепутанных байтов". 1С хранит GUID в формате Little-endian для определенных групп разрядов, что отличается от стандартного строкового представления.
При поиске в SQL строку AAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE нужно трансформировать. Первые три группы байтов (A, B и C) в бинарном поле _IDRRef хранятся в обратном порядке. Если этого не учитывать, прямой запрос к SQL-таблице по "красивой" строке УИД не вернет данных.
Мы выяснили, что "волшебной" функции, которая мгновенно скажет тип объекта по его УИД, не существует из-за архитектурных особенностей хранения данных. Однако, проанализировав ситуацию, мы можем выбрать оптимальный путь:
Всегда помните о производительности: Ссылка.ПолучитьОбъект() — это самый медленный способ проверки существования, старайтесь заменять его легкими запросами ВЫБРАТЬ 1.