Как организовать выборку и перенос данных из связанных таблиц через COM-соединение

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

Перенос данных между базами 1С с использованием технологии COM-соединения (Automation) — это классическая задача, с которой сталкиваются разработчики при необходимости синхронизации данных без использования сложных инструментов вроде «1С:Конвертация данных». Однако при работе со связанными справочниками и регистрами возникают нюансы: как сопоставить объекты, если ссылка в COM-базе — это не то же самое, что ссылка в текущей базе? Разберем подробно, как реализовать этот процесс эффективно и с минимальными потерями производительности.

Проблема объектных ссылок в COM-соединении

Проанализируем главную сложность: когда мы получаем объект через V83.ComConnector, мы работаем с прокси-объектом. Если в базе-источнике реквизит справочника ссылается на другой справочник, то через COM мы получим ссылку, которая «живет» только внутри того соединения. Мы не можем просто написать Приемник.Контрагент = ИсточникCOM.Контрагент, так как типы данных принадлежат разным пространствам имен.

Выясним причину, по которой прямое сравнение невозможно. Каждая ссылка в 1С характеризуется уникальным идентификатором (GUID). Чтобы сопоставить объекты в двух базах, нам нужно использовать именно этот идентификатор. Рассмотрим алгоритм сопоставления:

  1. В базе-источнике (через COM) получаем строковое представление GUID объекта.
  2. В базе-приемнике ищем объект по этому GUID или создаем новый с таким же идентификатором.

Посмотрим на пример кода, который реализует получение идентификатора через COM:


// На стороне базы-приемника
ИдентификаторСтрокой = БазаИсточникCOM.String(ОбъектИсточникCOM.UUID());
НоваяСсылка = Справочники.Контрагенты.ПолучитьСсылку(Новый УникальныйИдентификатор(ИдентификаторСтрокой));

Способ 1: Сопоставление по GUID и «ленивая» загрузка

Рассмотрим ситуацию, когда нам нужно перенести справочник «Договоры», который связан со справочником «Контрагенты». Чтобы не получить «битые» ссылки (объект не найден), мы должны применить двухпроходную систему или предварительное создание владельцев.

Разберем по шагам наиболее надежный метод:

Шаг 1: Подготовка данных на стороне источника. Вместо того чтобы перебирать объекты в цикле и обращаться к каждому свойству (что крайне медленно из-за сетевых задержек COM), выполним запрос на стороне COM-базы и выгрузим результат в таблицу значений.

Шаг 2: Обработка связанных данных. Для каждого связанного объекта (например, владельца договора) мы должны сначала проверить его наличие в нашей базе. Проанализируем пример функции для поиска/создания ссылки:


Функция НайтиСоздатьОбъектПоGUID(ИмяСправочника, ИдентификаторСтрокой, COMБаза)
    СсылкаВПриемнике = Справочники[ИмяСправочника].ПолучитьСсылку(Новый УникальныйИдентификатор(ИдентификаторСтрокой));
    
    // Проверяем, существует ли объект в базе
    Если СсылкаВПриемнике.ПолучитьОбъект() = Неопределено Тогда
        // Если нет — создаем или подтягиваем минимальные данные
        ОбъектИсточник = COMБаза.Справочники[ИмяСправочника].ПолучитьСсылку(COMБаза.NewObject("UUID", ИдентификаторСтрокой));
        // Здесь логика создания нового объекта...
    КонецЕсли;
    
    Возврат СсылкаВПриемнике;
КонецФункции

Способ 2: Оптимизация через пакетную выгрузку (JSON/XDTO)

Многие участники обсуждения справедливо замечают, что «чистый» COM работает медленно. Каждое обращение к свойству Объект.Наименование инициирует вызов через границу процессов. Чтобы ускорить процесс, проанализируем ситуацию: не лучше ли передавать данные пакетами?

Вместо ручного копирования реквизитов, воспользуемся сериализацией. Разберем этот подход:

  1. На стороне COM-базы формируем структуру или таблицу значений со всеми нужными данными (включая GUID всех ссылочных полей).
  2. Сериализуем эту структуру в строку JSON или XML (используя XDTO).
  3. Передаем одну большую строку в основную базу.
  4. В основной базе десериализуем данные и создаем объекты в транзакции.

Этот метод позволяет сократить количество COM-вызовов в тысячи раз. Посмотрим на пример формирования данных на стороне источника:


// Внутри COM-базы (вызывается из приемника)
Функция ПолучитьДанныеВФорматеJSON(ЗапросТекст) Экспорт
    Запрос = НовыйЗапрос(ЗапросТекст);
    ТЗ = Запрос.Выполнить().Выгрузить();
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, ТЗ);
    Возврат ЗаписьJSON.Закрыть();
КонецФункции

Способ 3: Использование таблицы соответствий

При работе с ведомыми таблицами (например, табличные части или зависимые справочники) удобно использовать кэширование в оперативной памяти. Рассмотрим подробнее, как это работает. Чтобы не обращаться к базе данных для поиска каждой ссылки, мы создаем объект Соответствие.

Ключом в этом соответствии будет GUID из базы-источника, а значением — готовая ссылка в базе-приемнике. Проанализируем алгоритм:

Это избавляет от избыточных запросов SELECT к базе данных приемника, особенно если у многих записей один и тот же владелец.

Рекомендации по стабильности соединения

Работа с COM-соединением требует аккуратности. Выясним причину возможных сбоев и как их предотвратить:

  1. Утечки памяти: Всегда явно обнуляйте переменные, содержащие COM-объекты (COMОбъект = Неопределено;), и вызывайте ЗавершитьРаботуСистемы(Ложь) для закрытия процесса, если это необходимо.
  2. Таймауты: При передаче очень больших объемов данных COM-соединение может «отвалиться». Используйте порционную загрузку (например, по 500-1000 элементов за один вызов).
  3. Версионность: Убедитесь, что на сервере или клиенте, где выполняется код, зарегистрирована соответствующая версия comcntr.dll.

Подводя итог, можно сказать: перенос данных через COM-соединение вполне жизнеспособен, если использовать UUID для синхронизации и минимизировать количество обращений к свойствам объектов, отдавая предпочтение пакетной передаче данных через структуры или JSON.

← На главную