В процессе разработки на платформе 1С:Предприятие часто возникает задача связать данные из разных источников. Одной из самых коварных проблем является попытка соединения таблиц по полю типа УникальныйИдентификатор и текстовому полю (или полю сложного типа, содержащему строку). На первый взгляд, в консоли запросов значения выглядят идентично, но при попытке сравнения система выдает ошибку: «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов». При необходимости можно воспользоваться руководством по сравнению строк и представлений ссылок в запросах 1С.
В этой статье мы подробно разберем, почему стандартные функции преобразования не работают в данном случае, и рассмотрим несколько надежных способов решения этой задачи. Если сопоставление требуется выполнить в процессе разовой миграции объектов, которые были заведены в базах вручную и имеют разные ссылки, можно запустить перенос данных между похожими конфигурациями 1С с поиском по произвольным реквизитам.
Давайте проанализируем ситуацию. В 1С тип УникальныйИдентификатор на уровне системы управления базами данных (СУБД) представляет собой 16-байтовое двоичное число (binary(16) или uniqueidentifier в MS SQL Server). В то же время строковое представление GUID — это текст из 36 символов. Для платформы это абсолютно разные сущности: «мальчик и девочка», как метко подметили участники обсуждения.
Функция ПРЕДСТАВЛЕНИЕ() в языке запросов предназначена исключительно для получения текстового описания объекта для нужд интерфейса. Она возвращает специальный тип, который нельзя использовать в условиях ГДЕ, конструкциях СОЕДИНЕНИЕ или СГРУППИРОВАТЬ ПО. Использовать ВЫРАЗИТЬ(Поле КАК СТРОКА) для типа УникальныйИдентификатор также не получится, так как платформа не поддерживает прямое приведение этого типа к строке внутри SQL-запроса.
Если нам необходимо выполнить разовый запрос или отчет, где данные связаны через GUID и строку, самым правильным подходом будет предварительная подготовка данных на стороне встроенного языка. Рассмотрим этот алгоритм по шагам.
Сначала мы выгружаем данные из первой таблицы, приводим идентификаторы к строковому виду в коде, а затем передаем готовую таблицу обратно в запрос. Проанализируем пример кода:
// 1. Получаем данные из документа с УИН
ЗапросПодготовка = Новый Запрос;
ЗапросПодготовка.Текст =
"ВЫБРАТЬ
| ДокументОбъект.Ссылка КАК Ссылка,
| ДокументОбъект.УникальныйИдентификатор КАК УИН_Объект
|ИЗ
| Документ.НашДокумент КАК ДокументОбъект";
ТаблицаДанных = ЗапросПодготовка.Выполнить().Выгрузить();
// 2. Создаем колонку со строковым представлением
ТаблицаДанных.Колонки.Добавить("УИН_Строка", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(36)));
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
// Преобразуем тип УникальныйИдентификатор в строку
СтрокаТЗ.УИН_Строка = Строка(СтрокаТЗ.УИН_Объект);
КонецЦикла;
// 3. Передаем подготовленную таблицу в основной запрос
ЗапросОсновной = Новый Запрос;
ЗапросОсновной.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
ЗапросОсновной.УстановитьПараметр("ТаблицаСвязи", ТаблицаДанных);
ЗапросОсновной.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТаблицаСвязи КАК ТаблицаСвязи;
|
|ВЫБРАТЬ
| ВТ.Ссылка,
| Регистр.Заказ
|ИЗ
| ВТ_Данные КАК ВТ
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.НашРегистр КАК Регистр
| ПО ВТ.УИН_Строка = Регистр.Заказ"; // Теперь типы совместимы (Строка = Строка)
Результат = ЗапросОсновной.Выполнить();
Этот метод надежен, так как гарантирует, что сравнение происходит на уровне строк. Однако помните о производительности: соединение по строковым полям длиной 36 символов работает медленнее, чем по ссылкам или числам.
Если связь по уникальному идентификатору требуется постоянно (например, в высоконагруженных отчетах или при регулярном обмене данными), мы рекомендуем изменить структуру метаданных. Постоянное преобразование типов в запросах «на лету» — это путь к деградации производительности базы данных. Если такую операцию требуется выполнять на постоянной основе в рамках синхронизации систем, целесообразно настроить регулярный обмен данными между базами 1С по расписанию.
Выясним причину: СУБД не может эффективно использовать индексы, если в условии соединения используются преобразования типов или сложные функции. Рассмотрим оптимальный алгоритм реализации:
УИН_Строка с типом Строка(36).ПередЗаписью пропишем заполнение этого поля:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ЭтоНовый() Или НЕ ЗначениеЗаполнено(УИН_Строка) Тогда
УИН_Строка = Строка(ЭтотОбъект.УникальныйИдентификатор());
КонецЕсли;
КонецПроцедуры
Теперь в любых запросах мы сможем напрямую связывать таблицы, используя это индексированное поле. Это обеспечит максимальную скорость выполнения запроса (Index Seek вместо Table Scan).
Иногда задача стоит иначе: нужно найти ссылку на документ, зная его GUID. В этом случае не обязательно использовать сложные соединения в запросе. Мы можем воспользоваться методом менеджера объекта ПолучитьСсылку().
Разберем пример, когда у нас есть строка с GUID из внешнего источника:
// Строка, полученная из регистра или внешнего файла
СтрокаGUID = "550e8400-e29b-41d4-a716-446655440000";
НовыйGUID = Новый УникальныйИдентификатор(СтрокаGUID);
// Получаем ссылку напрямую, зная тип документа
СсылкаНаДокумент = Документы.ЗаказПокупателя.ПолучитьСсылку(НовыйGUID);
// Важно проверить, существует ли такой объект в базе
Если СсылкаНаДокумент.ПолучитьОбъект() = Неопределено Тогда
Сообщить("Объект не найден!");
КонецЕсли;
Этот метод работает мгновенно и позволяет избежать написания громоздких запросов, если вам нужно обработать конкретный список идентификаторов.
Мы проанализировали ситуацию и выяснили, что прямое сравнение типа УникальныйИдентификатор и Строка в запросе невозможно из-за архитектуры 1С. Подведем итоги наших решений:
ТаблицаЗначений с ручным приведением типов и последующей передачей в МенеджерВременныхТаблиц.Использование уникальных идентификаторов в качестве ключей связи — это мощный инструмент, но он требует понимания того, как 1С работает с типами данных на физическом уровне СУБД. Следуя приведенным выше инструкциям, вы сможете избежать ошибок несовместимости типов и обеспечить стабильную работу ваших запросов.