Как обработать результат запроса к базе 1С через COM-соединение?

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

Работа с распределенными данными часто требует обращения к одной базе 1С из другой. Использование COM-соединения (Automation) — классический способ решения этой задачи. Однако новички часто сталкиваются с проблемой: запрос в удаленной базе выполняется успешно, но при попытке прочитать результат в текущей базе возникают ошибки или возвращаются пустые значения. Разберем детально, как правильно выгрузить и обработать данные, полученные через COMОбъект.

Причина проблемы: Разрыв контекста

Когда мы выполняем запрос через глСоединение.NewObject("Запрос"), объект результата запроса или выгруженная таблица значений физически находятся в памяти «той» базы (базы-источника). В нашей текущей базе мы имеем лишь «прокси-ссылку» на этот объект. Прямая попытка выполнить ЗначениеВСтрокуВнутр(vTrans) в текущем сеансе обращается к локальному менеджеру типов, который «не знает», как интерпретировать структуру объекта из другого процесса. Поэтому мы получаем Неопределено или ошибку итератора.

Решение 1: Сериализация через внутреннюю строку на стороне источника

Проанализируем самый быстрый в реализации способ — использование метода ЗначениеВСтрокуВнутр. Важный нюанс: этот метод должен быть вызван через объект соединения, чтобы сериализация прошла в контексте удаленной базы.

Рассмотрим пошаговый алгоритм:

  1. Выполняем запрос в удаленной базе.
  2. Выгружаем результат в таблицу значений (в памяти удаленной базы).
  3. Вызываем метод ЗначениеВСтрокуВнутр именно через COM-соединение.
  4. Полученную строку передаем в локальный метод ЗначениеИзСтрокиВнутр.

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


// 1. Инициализируем запрос в COM-базе
vQry = глСоединение.NewObject("Запрос");
vQry.Text = "ВЫБРАТЬ Номенклатура.Наименование, Номенклатура.Код ИЗ Справочник.Номенклатура КАК Номенклатура";

// 2. Получаем таблицу значений (она пока в COM-контексте)
vTrans = vQry.Execute().Unload();

// 3. СЕРИАЛИЗАЦИЯ: вызываем метод через глСоединение!
// Это заставит удаленную базу превратить свою ТЗ в строку
стрТЗ = глСоединение.ЗначениеВСтрокуВнутр(vTrans);

// 4. ДЕСЕРИАЛИЗАЦИЯ: используем локальную функцию
// Теперь в нашей базе появилась полноценная ТаблицаЗначений
ТЗ = ЗначениеИзСтрокиВнутр(стрТЗ);

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

Решение 2: Прямой обход выборки (классический способ)

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

Проанализируем правильный подход с использованием Выбрать() и Следующий():


РезультатЗапроса = vQry.Execute();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Важно: каждое обращение к Выборка.Наименование 
    // — это отдельный сетевой вызов (RPC), что может быть медленно
    Сообщить("Товар из другой базы: " + Выборка.Наименование);
КонецЦикла;

Если же вам необходимо работать именно с таблицей значений без сериализации, используйте обращение по индексу:


vTrans = vQry.Execute().Unload();
КолвоСтрок = vTrans.Count(); // Используем метод COM-объекта

Для Индекс = 0 По КолвоСтрок - 1 Цикл
    ТекущаяСтрока = vTrans.Get(Индекс);
    Значение = ТекущаяСтрока.Наименование;
    // Обработка данных...
КонецЦикла;

Проблема производительности при COM-вызовах

Выясним, почему предыдущий метод (цикл) может работать очень долго. Каждый раз, когда вы пишете Выборка.Наименование, программа делает «прыжок» в другую базу, запрашивает значение и возвращается назад. Если в результате 10 000 строк, таких «прыжков» будет десятки тысяч.

Рекомендация: Старайтесь минимизировать количество обращений через точку к COM-объекту. Лучше один раз передать большой объем данных (через строку или массив), чем многократно обращаться за маленькими кусочками.

Решение 3: Современный подход через JSON

Метод ЗначениеВСтрокуВнутр является платформозависимым. В современной практике для передачи данных между базами через COM-соединение лучше использовать JSON. Это быстрее и позволяет более гибко управлять типами.

Разберем, как это реализовать:

  1. В удаленной базе результат запроса упаковывается в массив структур.
  2. Массив преобразуется в строку JSON.
  3. В текущей базе строка JSON читается в локальную коллекцию.

// В удаленной базе (через COM) выполняем код формирования JSON
// Для этого можно использовать встроенные средства или вызвать функцию общего модуля
ТекстСкрипта = 
"
|Функция ПолучитьДанныеВJSON(Запрос)
|    ТЗ = Запрос.Execute().Unload();
|    ЗаписьJSON = Новый ЗаписьJSON;
|    ЗаписьJSON.УстановитьСтроку();
|    ЗаписатьJSON(ЗаписьJSON, ТЗ);
|    Возврат ЗаписьJSON.Закрыть();
|КонецФункции
|";

// Но проще всего сделать это так:
vQry = глСоединение.NewObject("Запрос");
vQry.Text = "ВЫБРАТЬ Код, Наименование ИЗ Справочник.Валюты";
vРезультат = vQry.Execute().Unload();

// Используем JSON на стороне COM-соединения
// (Предполагается, что в базе-источнике есть доступ к объекту ЗаписьJSON)
vЗаписьJSON = глСоединение.NewObject("ЗаписьJSON");
vЗаписьJSON.УстановитьСтроку();
глСоединение.ЗаписатьJSON(vЗаписьJSON, vРезультат);
СтрокаJSON = vЗаписьJSON.Закрыть();

// Теперь десериализуем в нашей базе
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
МассивДанных = ПрочитатьJSON(ЧтениеJSON); // Получаем массив структур

Как передать ссылки и UUID?

Если вам нужно передать не только текст, но и идентификаторы объектов, чтобы потом найти их в своей базе, используйте UUID. Ссылочный объект COM-базы бесполезен в текущей базе, но его строковое представление уникально.

Проанализируем пример запроса, возвращающего уникальный идентификатор:


vQry.Text = "ВЫБРАТЬ ВЫРАЗИТЬ(Ссылка КАК СТРОКА) КАК УИД_Строкой, Наименование ИЗ Справочник.Номенклатура";
// Или в цикле получать UUID:
СтрокаУИД = глСоединение.String(Выборка.Ссылка.UUID());

Итоги и рекомендации

Подведем итог нашим изысканиям. Для эффективной обработки результатов запроса через COM следует придерживаться следующих правил:

Соблюдение этих принципов позволит вам создавать быстрые и надежные механизмы обмена данными между базами 1С.

← На главную