Как быстро выгрузить данные со строковыми GUID из 1С в базу SQL?

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

При организации интеграции между 1С и внешними базами данных (СУБД SQL, WMS, CRM-системами) часто требуется передавать идентификаторы ссылочных объектов не в виде внутренних типов 1С или шестнадцатеричных представлений, а в виде стандартных строковых GUID (UUID по стандарту RFC 4122 формата 00000000-0000-0000-0000-000000000000). Построчное получение таких данных через COM-соединение или функциями вроде XMLСтрока() приводит к катастрофическому падению производительности на больших объемах данных. Разберем основные причины медленной работы и рассмотрим эффективные варианты ускорения процесса выгрузки.

Причины медленной работы и архитектурные ограничения

Проанализируем, почему типовые подходы работают медленно:

  1. Межпроцессный маршалинг (COM/RPC-вызовы): Если выгрузка инициируется внешней базой через COM-соединение с построчным обходом выборки (Выборка.Следующий(), Выборка.Ссылка, Выборка.Код), на каждое обращение к реквизиту тратится время на сетевой/системный вызов между процессами.
  2. Накладные расходы DOM-сериализации: Сериализация больших таблиц через СериализаторXDTO в XML-строку целиком загружает структуру документа в оперативную память, создавая чрезмерную нагрузку на сборщик мусора и приводя к утечкам памяти при отсутствии явного закрытия дескрипторов потоков.
  3. Построчная вставка в SQL: Выполнение единичных INSERT на каждую строку многократно нагружает транзакционный журнал СУБД.

Способ 1. Получение строкового GUID на стороне 1С перед передачей

Самый простой и надежный способ — выполнить всю выборку, добавление колонок и строковое приведение идентификаторов непосредственно на стороне базы 1С за один вызов, избегая межпроцессных обращений по COM в цикле.

Рассмотрим пример кода на платформе 1С:


Процедура ПолучитьТаблицуУИД()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   РеализацияТоваровУслуг.Ссылка КАК Док,
        |   РеализацияТоваровУслуг.СуммаДокумента КАК СуммаДокумента,
        |   РеализацияТоваровУслуг.Номер КАК Номер
        |ИЗ
        |   Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг";
    
    Таб = Запрос.Выполнить().Выгрузить();
    Таб.Колонки.Добавить("UID", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(36)));
    
    Для Каждого Стр Из Таб Цикл
        Стр.UID = Строка(Стр.Док.УникальныйИдентификатор());
    КонецЦикла;
    
КонецПроцедуры

При таком подходе цикл внутри контекста 1С выполняется за доли секунды на десятках тысяч записей, после чего готовая таблица с уже заполненными строковыми значениями передается дальше.

Способ 2. Прямое чтение из таблиц СУБД и конвертация binary(16) в GUID

В таблицах СУБД (например, MS SQL Server) поле ссылки _IDRRef хранится в виде binary(16). Порядок байтов в представлении 1С отличается от стандарта RFC 4122. Чтобы получить строковый GUID напрямую на стороне SQL без участия COM и платформы 1С, выполним преобразование порядка байтов с помощью скалярных функций и SUBSTRING.

Для сопоставления имен таблиц и полей метаданным 1С используем метод платформы ПолучитьСтруктуруХраненияБазыДанных(), после чего строим запрос к SQL-серверу:


SELECT 
    LOWER(
        SUBSTRING(sys.fn_varbintohexstr(_IDRRef), 27, 8) + '-' +
        SUBSTRING(sys.fn_varbintohexstr(_IDRRef), 23, 4) + '-' +
        SUBSTRING(sys.fn_varbintohexstr(_IDRRef), 19, 4) + '-' +
        SUBSTRING(sys.fn_varbintohexstr(_IDRRef), 3, 4) + '-' +
        SUBSTRING(sys.fn_varbintohexstr(_IDRRef), 7, 12)
    ) AS GuidString,
    _Number AS DocNumber
FROM _Document123

Такой подход позволяет объединить чтение и запись в одну быструю операцию INSERT INTO ... SELECT напрямую между базами на уровне СУБД.

Способ 3. Побитовое преобразование двоичных данных в 1С

Если необходимо обработать шестнадцатеричное представление или бинарный буфер средствами платформы 1С (начиная с 8.3.10), можно использовать объект БуферДвоичныхДанных. Разберем по шагам алгоритм сдвига и маскирования байтов:


ПБ = ПорядокБайтов.BigEndian;
УИДБуфер = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ГексСтрингУИД);
УИДБуфер.ПорядокБайтов = ПБ;

СтаршиеБиты = УИДБуфер.Прочитать(0, 8);
МладшиеБиты = УИДБуфер.Прочитать(8, 8);

Маска5 = Новый БуферДвоичныхДанных(8, ПБ);
Маска5.ЗаписатьЦелое64(0, 4294967295, ПБ);
Группа5 = МладшиеБиты.Скопировать();
Группа5.ЗаписатьПобитовоеИ(0, Маска5);
СдвинутыйБуфер5 = Новый БуферДвоичныхДанных(16, ПБ);
СдвинутыйБуфер5.Записать(4, Группа5);
СдвинутыйБуфер5 = СдвинутыйБуфер5.Прочитать(8, 8);

Маска4 = Новый БуферДвоичныхДанных(8, ПБ);
Маска4.ЗаписатьЦелое64(0, ЧислоИзШестнадцатеричнойСтроки("0xFFFF00000000"), ПБ);
Группа4 = МладшиеБиты.Скопировать();
Группа4.ЗаписатьПобитовоеИ(0, Маска4);
СдвинутыйБуфер4 = Новый БуферДвоичныхДанных(16, ПБ);
СдвинутыйБуфер4.Записать(10, Группа4);
СдвинутыйБуфер4 = СдвинутыйБуфер4.Прочитать(8, 8);

Маска3 = Новый БуферДвоичныхДанных(8, ПБ);
Маска3.ЗаписатьЦелое64(0, ЧислоИзШестнадцатеричнойСтроки("0xFFFF000000000000"), ПБ);
Группа3 = МладшиеБиты.Скопировать();
Группа3.ЗаписатьПобитовоеИ(0, Маска3);
СдвинутыйБуфер3 = Новый БуферДвоичныхДанных(16, ПБ);
СдвинутыйБуфер3.Записать(14, Группа3);
СдвинутыйБуфер3 = СдвинутыйБуфер3.Прочитать(8, 8);

НоваяСтаршаяГруппа = Новый БуферДвоичныхДанных(8, ПБ);
НоваяСтаршаяГруппа.ЗаписатьПобитовоеИли(0, СдвинутыйБуфер3);
НоваяСтаршаяГруппа.ЗаписатьПобитовоеИли(0, СдвинутыйБуфер4);
НоваяСтаршаяГруппа.ЗаписатьПобитовоеИли(0, СдвинутыйБуфер5);

ПолныйБуфер = Новый БуферДвоичныхДанных(16, ПБ);
ПолныйБуфер.Записать(0, НоваяСтаршаяГруппа);
ПолныйБуфер.Записать(8, СтаршиеБиты);

ПравильныйУИД = НРег(
    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(0, 4)) + "-" +
    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(4, 2)) + "-" +
    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(6, 2)) + "-" +
    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(8, 2)) + "-" +
    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(10, 6)));

Способ 4. Использование потоковой сериализации JSON

Если данные передаются между системами в виде файлов или текстовых потоков, откажемся от сложного разбора XML в пользу ЗаписьJSON и СериализаторXDTO.ЗаписатьJSON(). В отличие от XML, потоковый JSON обрабатывается быстрее, не создает перегрузки оперативной памяти и позволяет выгружать массивы любого размера без необходимости разбивать их на мелкие порции по 1 000 строк.

Способ 5. Использование стандартного интерфейса OData (REST API)

Начиная с версии 8.3.5, в платформе 1С присутствует встроенный REST-интерфейс OData. При его включении отпадает необходимость вручную преобразовывать ссылки: все сущности возвращаются в формате JSON, где ключевые ссылочные реквизиты уже представлены в виде стандартных строковых GUID (поле Ref_Key).

Рекомендации по ускорению загрузки в целевую СУБД

  1. Пакетная вставка: Не отправляйте единичные команды INSERT. Группируйте строки в конструкции вида INSERT INTO Table (Col1, Col2) VALUES (...), (...), (...) порциями по 500–1000 строк внутри одной транзакции.
  2. BULK INSERT: Для максимальной скорости формируйте временный CSV-файл и выполняйте операцию BULK INSERT на стороне SQL-сервера, что обеспечивает скорость импорта до сотен тысяч записей в секунду.
← На главную