При работе с внешними информационными базами 1С через COM-соединение разработчики часто сталкиваются с ошибкой «Несоответствие типов (параметр №1)» при вызове метода Выбрать() у объекта РезультатЗапроса. Ошибка возникает в тот момент, когда требуется выполнить обход по группировкам или иерархии, передав соответствующий режим обхода.
В этой статье мы подробно проанализируем ситуацию, выясним причину возникновения такой ошибки и разберем по шагам правильные способы работы с запросами, выборками и типами данных через COM-интерфейс.
Проанализируем ситуацию с несовместимостью типов данных. В платформе 1С:Предприятие системные перечисления (такие как ОбходРезультатаЗапроса, РежимПроведения и другие), а также объекты метаданных и ссылки принадлежат контексту конкретной информационной базы.
Когда мы выполняем следующий код в текущей (локальной) базе:
Выборка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Перечисление ОбходРезультатаЗапроса.ПоГруппировкам берется из локального контекста текущего сеанса. Однако сам объект РезультатЗапроса был получен из внешнего COM-соединения. Для объекта внешней базы локальное перечисление является чужеродным типом. Попытка передать его в метод внешнего объекта приводит к ошибке несоответствия типов в параметре №1.
Эта же проблема возникнет и при передаче ссылочных параметров во внешний запрос через метод УстановитьПараметр(). Нельзя передавать ссылку или массив ссылок из текущей базы во внешний запрос. Все ссылочные объекты и перечисления должны быть получены непосредственно из внешней базы (например, через Соединение.Справочники... НайтиПоКоду()) или передаваться в виде примитивных типов (числа, строки, даты, булево).
Рассмотрим подробнее решение проблемы. Для того чтобы платформой корректно воспринимался параметр режима обхода выборки, системное перечисление необходимо запрашивать непосредственно из контекста подключенного COM-объекта.
Разберем по шагам правильный алгоритм подключения и выполнения запроса с обходом по группировкам:
Коннектор = Новый COMObject("V83.COMConnector");
Попытка
Соединение = Коннектор.Connect("File=""C:\Bases\RemoteBase"";Usr=""Admin"";Pwd=""123"";");
Исключение
Сообщить("Не удалось подключиться к внешней ИБ: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
ЗапросВнешний = Соединение.NewObject("Запрос");
ЗапросВнешний.Текст =
"ВЫБРАТЬ
| Продажи.Контрагент КАК Контрагент,
| Продажи.Номенклатура КАК Номенклатура,
| СУММА(Продажи.Сумма) КАК Сумма
|ИЗ
| РегистрНакопления.Продажи КАК Продажи
|СГРУППИРОВАТЬ ПО
| ИТОГИ СУММА(Сумма) ПО Контрагент, Номенклатура";
РезультатЗапроса = ЗапросВнешний.Выполнить();
// Используем перечисление из объекта Соединение
ВыборкаКонтрагент = РезультатЗапроса.Выбрать(Соединение.ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаКонтрагент.Следующий() Цикл
Сообщить("Контрагент: " + ВыборкаКонтрагент.Контрагент + ", Сумма: " + ВыборкаКонтрагент.Сумма);
// Получаем вложенную выборку номенклатуры по группировке
ВыборкаНоменклатура = ВыборкаКонтрагент.Выбрать(Соединение.ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНоменклатура.Следующий() Цикл
Сообщить(" Номенклатура: " + ВыборкаНоменклатура.Номенклатура + ", Сумма: " + ВыборкаНоменклатура.Сумма);
КонецЦикла;
КонецЦикла;
Посмотрим на пример вариантов вызова различных режимов обхода выборки через внешний COM-объект:
Соединение.ОбходРезультатаЗапроса.ПрямойСоединение.ОбходРезультатаЗапроса.ПоГруппировкамСоединение.ОбходРезультатаЗапроса.ПоГруппировкамСИерархиейВыясним причину, почему построчный обход выборки через COM может существенным образом замедлять работу системы, и рассмотрим эффективные альтернативы.
При построчном обходе (Пока Выборка.Следующий() Цикл) и каждом обращении к свойствам (Выборка.Реквизит) платформа генерирует отдельный межпроцессный или сетевой COM-вызов. На больших объемах данных это приводит к существенным падениям производительности. Кроме того, открытое COM-соединение удерживает клиентскую лицензию 1С на все время работы.
Для оптимизации работы с внешними данными рекомендуется использовать следующие решения:
ЗначениеВСтрокуВнутр:
Выполняем запрос во внешней базе, выгружаем результат в таблицу значений и сериализуем его в текстовую строку на стороне внешнего соединения. После этого передаем строку в локальную базу и восстанавливаем из нее локальную таблицу значений.
// На стороне внешней базы через COM:
ТаблицаВнешняя = РезультатЗапроса.Выгрузить();
СтрокаДанных = Соединение.ЗначениеВСтрокуВнутр(ТаблицаВнешняя);
// На стороне текущей базы:
ТаблицаЛокальная = ЗначениеИзСтрокиВнутр(СтрокаДанных);
Этот способ позволяет передать весь массив данных за один вызов и обходить иерархию или группировки уже локально с максимальной скоростью.
Формируем структуры или массивы на стороне внешнего соединения и сериализуем их в JSON-строку. В текущей базе декодируем строку обратно в объекты.
Вместо COM-соединения настраивается HTTP-сервис во внешней информационной базе, который возвращает сгруппированные данные в формате JSON. Это исключает зависимости от COM-компонентов и эффективно расходует лицензии.
В подключаемой базе создается процедура или функция в общем модуле, которая самостоятельно выполняет запрос, обрабатывает группировки и возвращает готовый результат в виде примитивных типов или структуры.