При разработке сложных интеграционных механизмов или кастомных регистров в 1С:Предприятии программисты часто сталкиваются с необходимостью связать данные по уникальному идентификатору (GUID). Типичная ситуация: в табличной части документа или во внешней системе хранится строковое представление GUID, а в новом регистре накопления или сведений это поле создано с типом УникальныйИдентификатор. Попытка связать их напрямую в запросе или использовать конструкцию ВЫРАЗИТЬ приводит к ошибке несовместимости типов. Разберем по шагам, почему так происходит, и рассмотрим эффективные практические способы решения этой проблемы.
Многие начинающие разработчики пытаются решить задачу «в лоб», применяя функцию приведения типов прямо в тексте запроса. Схема выглядит следующим образом:
ПО ВЫРАЗИТЬ(ДанныеДокумента.УникальныйИдентификаторУслуги КАК Строка(36)) = ПродажиОбороты.УникальныйИдентификаторУслуги
При попытке выполнить такой запрос платформа выдаст ошибку «Несовместимые типы ВЫРАЗИТЬ». Дело в том, что функция ВЫРАЗИТЬ в языке запросов 1С предназначена исключительно для ограничения типов у полей составного типа, а не для полноценного преобразования (кастинга) одного примитивного типа в другой.
На уровне СУБД (например, MS SQL Server или PostgreSQL) типы данных УникальныйИдентификатор и Строка хранятся принципиально по-разному. Идентификатор хранится в виде компактного 16-байтового бинарного значения, тогда как строка длиной 36 символов занимает в разы больше места в юникоде. Платформа 1С не умеет на лету транслировать преобразование бинарного формата в строку внутри SQL-запроса, так как это привело бы к колоссальному падению производительности и невозможности использования индексов СУБД при соединении таблиц.
Если задача находится на этапе проектирования или у вас есть возможность скорректировать метаданные, лучшим решением будет полный отказ от хранения УИД в виде «сырых» строк или системных идентификаторов в измерениях регистров. Проанализируем ситуацию: если у вас есть некая уникальная сущность (например, «Услуга»), которая должна иметь свои статусы, историю и уникальные атрибуты, то эта сущность является объектной.
Вместо записи УИД в регистры создайте новый справочник (например, Справочник.УникальныеУслуги). При первом обращении/заказе услуги программно создавайте элемент этого справочника, где в качестве кода или наименования пишите ваш GUID (или даже генерируйте стандартную ссылку 1С). Во всех документах и регистрах в качестве типа реквизитов и измерений используйте СправочникСсылка.УникальныеУслуги.
Преимущества этого подхода:
Если метаданные конфигурации уже «заморожены», система находится в промышленной эксплуатации и переделывать архитектуру под справочники слишком трудоемко, можно воспользоваться классическим методом двухэтапного запроса с использованием промежуточной ТаблицыЗначений.
Рассмотрим по шагам данный алгоритм:
УникальныйИдентификатор) обычным запросом и выгружаем результат в ТаблицуЗначений.Строка(36).Посмотрим на пример реализации в коде:
// Шаг 1: Выбираем данные документа с УИД
Запрос1 = Новый Запрос;
Запрос1.Текст =
"ВЫБРАТЬ
| ЗаказПокупателяУслуги.Ссылка КАК ДокументСсылка,
| ЗаказПокупателяУслуги.УникальныйИдентификаторУслуги КАК УИД_Исходный
|ИЗ
| Документ.ЗаказПокупателя.Услуги КАК ЗаказПокупателяУслуги
|ГДЕ
| ЗаказПокупателяУслуги.Ссылка = &Ссылка";
Запрос1.УстановитьПараметр("Ссылка", ДокументСсылка);
ТаблицаДанных = Запрос1.Выполнить().Выгрузить();
// Шаг 2: Добавляем колонку для строкового представления
ТаблицаДанных.Колонки.Добавить("УИД_Строка", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(36)));
// Шаг 3: Приводим типы в коде 1С
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
СтрокаТЗ.УИД_Строка = Строка(СтрокаТЗ.УИД_Исходный);
КонецЦикла;
// Шаг 4: Передаем ТЗ во второй запрос и соединяем с регистром накопления
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// Помещаем подготовленную ТЗ во временную таблицу
Запрос2.Текст =
"ВЫБРАТЬ
| ТЗ.ДокументСсылка,
| ТЗ.УИД_Строка
|ПОМЕСТИТЬ ВТ_ДанныеДокумента
|ИЗ
| &ТаблицаДанных КАК ТЗ";
Запрос2.УстановитьПараметр("ТаблицаДанных", ТаблицаДанных);
Запрос2.Выполнить();
// Выполняем соединение по строковому полю
Запрос2.Текст =
"ВЫБРАТЬ
| ВТ.ДокументСсылка,
| ПродажиОбороты.СуммаОборот
|ИЗ
| ВТ_ДанныеДокумента КАК ВТ
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты КАК ПродажиОбороты
| ПО ВТ.УИД_Строка = ПродажиОбороты.УникальныйИдентификаторУслугиСтрока"; // Здесь должно быть строковое измерение
Результат = Запрос2.Выполнить().Выбрать();
Если соединение по УИД требуется выполнять очень часто в тяжелых отчетах, где выгрузка во временные таблицы критично снижает производительность, можно применить «костыль оптимизации» — дублирование данных.
Для этого выполним следующие действия:
УникальныйИдентификаторСтрока) с типом Строка(36).ПередЗаписью прописываем автоматическое заполнение этого поля:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Для Каждого СтрокаТЧ Из Услуги Цикл
СтрокаТЧ.УникальныйИдентификаторСтрока = Строка(СтрокаТЧ.УникальныйИдентификатор);
КонецЦикла;
КонецПроцедуры
После проведения однократной групповой обработки старых документов для заполнения нового строкового реквизита, вы сможете писать прямые, быстрые и лаконичные запросы, соединяя строковое поле документа со строковым полем регистра. СУБД будет использовать стандартные индексы, и быстродействие системы останется на высоком уровне.
Начиная с версии платформы 1С:Предприятие 8.3.22, в язык запросов была добавлена встроенная функция УникальныйИдентификатор(Ссылка). С ее помощью можно получить УИД любой ссылки непосредственно внутри запроса:
ВЫБРАТЬ
УникальныйИдентификатор(Номенклатура.Ссылка) КАК УИД_Номенклатуры
ИЗ
Справочник.Номенклатура КАК Номенклатура
Однако помните, что эта функция возвращает значение системного типа УникальныйИдентификатор, а не Строку. Поэтому она не решает проблему прямого сравнения со строковыми полями без использования промежуточных временных таблиц, рассмотренных в Способе 2, но существенно упрощает получение самого УИДа из ссылочных типов прямо в теле запроса.