При интеграции различных конфигураций 1С (например, соединение «Охрана труда» и «УПП») разработчики часто сталкиваются с необходимостью выполнения запросов во внешней базе через COMConnector. Одной из самых коварных ошибок является ситуация, когда запрос корректно работает в консоли запросов или внутри конфигурации, но при попытке передать параметр через COM-соединение возникает исключение: «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов». Проанализируем, почему это происходит, и разберем по шагам, как правильно организовать работу с параметрами запроса во внешних базах.
Разберем ситуацию: мы создаем объект Запрос во внешней базе через БазаИсточник.NewObject("Запрос"). Когда мы пытаемся установить параметр, используя ссылку из текущей (локальной) базы, например, Запрос.УстановитьПараметр("СсылкаДок", т.Документ), мы передаем объект, который «живет» в памяти текущего сеанса. Для внешней базы, подключенной через COM, эта ссылка является абсолютно чужеродным объектом. Несмотря на то, что визуально GUID (уникальный идентификатор) может совпадать, типы данных ДокументСсылка в локальной базе и во внешней базе — это разные типы с точки зрения платформы 1С.
В результате, когда SQL-движок внешней базы пытается выполнить сравнение в блоке ГДЕ, он не может сопоставить локальную ссылку с данными в своих таблицах. Для него это «неопознанный объект», что и приводит к ошибке Несовместимые типы.
Самый простой способ, который обсуждался на форуме, — это получение ссылки непосредственно из объекта БазаИсточник. Чтобы запрос во внешней базе отработал, параметр &СсылкаДок должен быть COM-объектом, созданным именно в той базе, где выполняется запрос.
Рассмотрим пример правильной передачи ссылки:
// Предположим, у нас есть локальная ссылка СсылкаЛокальная
// Нам нужно найти такую же ссылку во внешней базе БазаИсточник
// Способ через поиск по коду или номеру (не самый надежный, но наглядный)
СсылкаДляЗапроса = БазаИсточник.Справочники.Номенклатура.НайтиПоКоду(СсылкаЛокальная.Код);
// Теперь устанавливаем эту "внешнюю" ссылку в запрос
Запрос = БазаИсточник.NewObject("Запрос");
Запрос.Текст =
"ВЫБРАТЬ
| КатегорииОбъектов.Категория
|ИЗ
| РегистрСведений.КатегорииОбъектов КАК КатегорииОбъектов
|ГДЕ
| КатегорииОбъектов.Объект = &СсылкаДок";
Запрос.УстановитьПараметр("СсылкаДок", СсылкаДляЗапроса);
Выборка = Запрос.Выполнить().Выбрать();
Поиск по коду или наименованию ненадежен. Лучшим способом является передача UUID. Ссылки в разных базах (если это узлы РИБ или копии) имеют одинаковые идентификаторы. Мы можем извлечь UUID из локальной ссылки и воссоздать ссылку во внешней базе.
Разберем этот алгоритм подробнее:
UUID локального объекта с помощью метода Строка(Ссылка.УникальныйИдентификатор()).UUID из этой строки.ПолучитьСсылку() у соответствующего менеджера объекта во внешней базе.Посмотрим на пример кода:
// 1. Получаем строковый GUID из локальной базы
ЛокальныйGUID = Строка(т.Документ.УникальныйИдентификатор());
// 2. Создаем объект UUID во внешней базе через COM
ВнешнийUUID = БазаИсточник.NewObject("UUID", ЛокальныйGUID);
// 3. Получаем ссылку во внешней базе (нужно знать тип документа)
// Если это документ ЗаказПокупателя:
СсылкаДляЗапроса = БазаИсточник.Документы.ЗаказПокупателя.ПолучитьСсылку(ВнешнийUUID);
// 4. Теперь передаем в параметр
Запрос.УстановитьПараметр("СсылкаДок", СсылкаДляЗапроса);
Этот метод гарантирует, что тип данных параметра будет полностью совместим с метаданными внешней базы.
Если тип объекта заранее неизвестен (например, при работе с составными типами данных), можно использовать механизм XML-сериализации. Это универсальный способ «протащить» объект через границу COM-соединения.
Проанализируем шаги:
// В локальной базе преобразуем ссылку в XML-строку
СтрокаXML = XMLСтрока(т.Документ);
// Во внешней базе восстанавливаем ссылку из XML-строки
// Для этого нужно знать имя типа, например "ДокументСсылка.РеализацияТоваровУслуг"
ТипОбъекта = "ДокументСсылка." + т.Документ.Метаданные().Имя;
СсылкаДляЗапроса = БазаИсточник.XMLЗначение(БазаИсточник.Тип(ТипОбъекта), СтрокаXML);
Запрос.УстановитьПараметр("СсылкаДок", СсылкаДляЗапроса);
В типовых конфигурациях, таких как УПП, поле Объект в регистре КатегорииОбъектов имеет составной тип. При работе через COM это может вызвать дополнительные сложности с определением типа на стороне SQL. Чтобы помочь системе правильно сопоставить типы, рекомендуется использовать в тексте запроса функцию ВЫРАЗИТЬ.
Пример оптимизированного текста запроса:
Запрос.Текст =
"ВЫБРАТЬ
| КатегорииОбъектов.Объект,
| КатегорииОбъектов.Категория
|ИЗ
| РегистрСведений.КатегорииОбъектов КАК КатегорииОбъектов
|ГДЕ
| ВЫРАЗИТЬ(КатегорииОбъектов.Объект КАК Документ.ЗаказПокупателя) = &СсылкаДок";
Это явное приведение типов исключает неоднозначность при сравнении параметров в COM-запросе.
Выполнение Запрос.Выполнить() внутри цикла (как в примере автора темы) — это крайне ресурсоемкая операция. Каждое обращение через COM требует времени на маршалинг данных между процессами. Если у вас 1000 документов, выполнение 1000 отдельных запросов может занять минуты, в то время как один запрос с передачей массива данных отработает за секунды.
Как передать массив ссылок во внешнюю базу:
ВнешнийМассив = БазаИсточник.NewObject("Массив");.UUID (как в Решении №2).В (&МассивСсылок).ТаблицуЗначений: ТЗРезультат = Запрос.Выполнить().Выгрузить();.Работа с Выборка через COM происходит медленно. Гораздо эффективнее выгрузить результат в таблицу значений на стороне COM-сервера, а затем передать её целиком или перебирать её строки, так как количество «транзакций» между базами сократится.
Если вы работаете на 64-разрядном сервере 1С, вы можете столкнуться с тем, что COMConnector не зарегистрирован или недоступен (ошибка «Класс не зарегистрирован»). Это связано с тем, что библиотека comcntr.dll должна быть правильно прописана в компонентах COM+. Для решения этой проблемы создайте в «Службах компонентов» Windows пустое приложение COM+, добавьте в него компонент comcntr.dll и установите права доступа для пользователя, от имени которого запущен сервер 1С.