Как передать ссылку в запрос через COMConnector и избежать ошибки несовместимости типов

Программист 1С v8.3 (Обычные формы)
← На главную

При интеграции различных конфигураций 1С (например, соединение «Охрана труда» и «УПП») разработчики часто сталкиваются с необходимостью выполнения запросов во внешней базе через COMConnector. Одной из самых коварных ошибок является ситуация, когда запрос корректно работает в консоли запросов или внутри конфигурации, но при попытке передать параметр через COM-соединение возникает исключение: «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов». Проанализируем, почему это происходит, и разберем по шагам, как правильно организовать работу с параметрами запроса во внешних базах.

Почему возникает ошибка несовместимости типов?

Разберем ситуацию: мы создаем объект Запрос во внешней базе через БазаИсточник.NewObject("Запрос"). Когда мы пытаемся установить параметр, используя ссылку из текущей (локальной) базы, например, Запрос.УстановитьПараметр("СсылкаДок", т.Документ), мы передаем объект, который «живет» в памяти текущего сеанса. Для внешней базы, подключенной через COM, эта ссылка является абсолютно чужеродным объектом. Несмотря на то, что визуально GUID (уникальный идентификатор) может совпадать, типы данных ДокументСсылка в локальной базе и во внешней базе — это разные типы с точки зрения платформы 1С.

В результате, когда SQL-движок внешней базы пытается выполнить сравнение в блоке ГДЕ, он не может сопоставить локальную ссылку с данными в своих таблицах. Для него это «неопознанный объект», что и приводит к ошибке Несовместимые типы.

Решение №1: Получение ссылки в контексте внешней базы

Самый простой способ, который обсуждался на форуме, — это получение ссылки непосредственно из объекта БазаИсточник. Чтобы запрос во внешней базе отработал, параметр &СсылкаДок должен быть COM-объектом, созданным именно в той базе, где выполняется запрос.

Рассмотрим пример правильной передачи ссылки:


// Предположим, у нас есть локальная ссылка СсылкаЛокальная
// Нам нужно найти такую же ссылку во внешней базе БазаИсточник

// Способ через поиск по коду или номеру (не самый надежный, но наглядный)
СсылкаДляЗапроса = БазаИсточник.Справочники.Номенклатура.НайтиПоКоду(СсылкаЛокальная.Код);

// Теперь устанавливаем эту "внешнюю" ссылку в запрос
Запрос = БазаИсточник.NewObject("Запрос");
Запрос.Текст = 
    "ВЫБРАТЬ
    |   КатегорииОбъектов.Категория
    |ИЗ
    |   РегистрСведений.КатегорииОбъектов КАК КатегорииОбъектов
    |ГДЕ
    |   КатегорииОбъектов.Объект = &СсылкаДок";

Запрос.УстановитьПараметр("СсылкаДок", СсылкаДляЗапроса);
Выборка = Запрос.Выполнить().Выбрать();

Решение №2: Использование Уникального Идентификатора (UUID)

Поиск по коду или наименованию ненадежен. Лучшим способом является передача UUID. Ссылки в разных базах (если это узлы РИБ или копии) имеют одинаковые идентификаторы. Мы можем извлечь UUID из локальной ссылки и воссоздать ссылку во внешней базе.

Разберем этот алгоритм подробнее:

  1. Получаем строковое представление UUID локального объекта с помощью метода Строка(Ссылка.УникальныйИдентификатор()).
  2. Во внешней базе создаем новый объект UUID из этой строки.
  3. Используем метод ПолучитьСсылку() у соответствующего менеджера объекта во внешней базе.

Посмотрим на пример кода:


// 1. Получаем строковый GUID из локальной базы
ЛокальныйGUID = Строка(т.Документ.УникальныйИдентификатор());

// 2. Создаем объект UUID во внешней базе через COM
ВнешнийUUID = БазаИсточник.NewObject("UUID", ЛокальныйGUID);

// 3. Получаем ссылку во внешней базе (нужно знать тип документа)
// Если это документ ЗаказПокупателя:
СсылкаДляЗапроса = БазаИсточник.Документы.ЗаказПокупателя.ПолучитьСсылку(ВнешнийUUID);

// 4. Теперь передаем в параметр
Запрос.УстановитьПараметр("СсылкаДок", СсылкаДляЗапроса);

Этот метод гарантирует, что тип данных параметра будет полностью совместим с метаданными внешней базы.

Решение №3: Сериализация через XML

Если тип объекта заранее неизвестен (например, при работе с составными типами данных), можно использовать механизм XML-сериализации. Это универсальный способ «протащить» объект через границу COM-соединения.

Проанализируем шаги:


// В локальной базе преобразуем ссылку в XML-строку
СтрокаXML = XMLСтрока(т.Документ);

// Во внешней базе восстанавливаем ссылку из XML-строки
// Для этого нужно знать имя типа, например "ДокументСсылка.РеализацияТоваровУслуг"
ТипОбъекта = "ДокументСсылка." + т.Документ.Метаданные().Имя;
СсылкаДляЗапроса = БазаИсточник.XMLЗначение(БазаИсточник.Тип(ТипОбъекта), СтрокаXML);

Запрос.УстановитьПараметр("СсылкаДок", СсылкаДляЗапроса);

Проблема составных типов данных в регистрах

В типовых конфигурациях, таких как УПП, поле Объект в регистре КатегорииОбъектов имеет составной тип. При работе через COM это может вызвать дополнительные сложности с определением типа на стороне SQL. Чтобы помочь системе правильно сопоставить типы, рекомендуется использовать в тексте запроса функцию ВЫРАЗИТЬ.

Пример оптимизированного текста запроса:


Запрос.Текст = 
    "ВЫБРАТЬ
    |   КатегорииОбъектов.Объект,
    |   КатегорииОбъектов.Категория
    |ИЗ
    |   РегистрСведений.КатегорииОбъектов КАК КатегорииОбъектов
    |ГДЕ
    |   ВЫРАЗИТЬ(КатегорииОбъектов.Объект КАК Документ.ЗаказПокупателя) = &СсылкаДок";

Это явное приведение типов исключает неоднозначность при сравнении параметров в COM-запросе.

Оптимизация производительности: избегаем запросов в цикле

Выполнение Запрос.Выполнить() внутри цикла (как в примере автора темы) — это крайне ресурсоемкая операция. Каждое обращение через COM требует времени на маршалинг данных между процессами. Если у вас 1000 документов, выполнение 1000 отдельных запросов может занять минуты, в то время как один запрос с передачей массива данных отработает за секунды.

Как передать массив ссылок во внешнюю базу:

  1. Создайте массив во внешней базе: ВнешнийМассив = БазаИсточник.NewObject("Массив");.
  2. Заполните его ссылками, полученными через UUID (как в Решении №2).
  3. Передайте этот массив в параметр запроса и используйте условие В (&МассивСсылок).
  4. Выгрузите результат запроса во внешнюю ТаблицуЗначений: ТЗРезультат = Запрос.Выполнить().Выгрузить();.

Работа с Выборка через COM происходит медленно. Гораздо эффективнее выгрузить результат в таблицу значений на стороне COM-сервера, а затем передать её целиком или перебирать её строки, так как количество «транзакций» между базами сократится.

Технический нюанс: 64-битные системы

Если вы работаете на 64-разрядном сервере 1С, вы можете столкнуться с тем, что COMConnector не зарегистрирован или недоступен (ошибка «Класс не зарегистрирован»). Это связано с тем, что библиотека comcntr.dll должна быть правильно прописана в компонентах COM+. Для решения этой проблемы создайте в «Службах компонентов» Windows пустое приложение COM+, добавьте в него компонент comcntr.dll и установите права доступа для пользователя, от имени которого запущен сервер 1С.

← На главную