Как быстро выгрузить миллион строк из таблицы 1С в файл и не сойти с ума

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

Выгрузка больших объемов данных из систем «1С:Предприятие» — классическая задача, с которой рано или поздно сталкивается любой разработчик. Это может быть выгрузка проводок для внешнего аудита, подготовка датасетов для машинного обучения (Machine Learning) или миграция данных в другую систему. Когда объем записей превышает миллион строк, стандартные подходы «в лоб» начинают безбожно тормозить, съедать всю оперативную память на сервере и выполняться долгими часами.

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

Выясняем причину: почему стандартный обход выборки работает медленно

Типичная ошибка начинающего разработчика — написание стандартного цикла обхода выборки запроса с обращением к полям по их имени. Проанализируем типичный фрагмент кода:


Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
    ЗначениеПоля = Выборка.ИмяНужногоПоля; // Или Выборка["ИмяНужногоПоля"]
    // Запись в файл...
КонецЦикла;

Замеры производительности с помощью профайлера показывают, что при работе с миллионными выборками до 80–90% процессорного времени тратится не на запись в файл и даже не на чтение данных из СУБД, а именно на получение значений из выборки через Выборка[Имя_нужного_поля].

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

Рассмотрим рабочие альтернативные варианты, которые позволяют решить эту проблему в разы быстрее.

Решение 1. Использование Таблицы значений вместо Выборки (Самый простой и эффективный способ)

Один из самых простых и при этом мощных способов оптимизации — полный отказ от обхода объекта типа РезультатЗапросаВыборка в пользу работы со статической структурой ТаблицаЗначений.

Метод Выгрузить() у объекта РезультатЗапроса реализован на уровне платформы на высокопроизводительном C++ коде. Платформа мгновенно забирает весь пакет данных из СУБД и формирует таблицу в оперативной памяти сервера приложений. Обход строк в таблице значений выполняется значительно быстрее, так как структура колонок уже жестко зафиксирована, а обращения к ячейкам оптимизированы.

Разберем по шагам правильный алгоритм реализации этого метода:


// Шаг 1. Получаем данные запросом
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ИЗ РегистрБухгалтерии.Проводки"; // Условный запрос
РезультатЗапроса = Запрос.Выполнить();

// Шаг 2. Выгружаем данные в Таблицу Значений (работает мгновенно на уровне C++)
ТаблицаДанных = РезультатЗапроса.Выгрузить();

// Шаг 3. Инициализируем запись текстового файла
ЗаписьТекста = Новый ЗаписьТекста;
ЗаписьТекста.Открыть("C:\Temp\export.csv", КодировкаТекста.UTF8, Символы.ПС);

// Шаг 4. Обходим готовую таблицу значений
Для Каждого СтрокаТаблицы Из ТаблицаДанных Цикл
    // Собираем строку с разделителями (например, точка с запятой)
    СтрокаВыгрузки = "";
    Для Каждого Колонка Из ТаблицаДанных.Колонки Цикл
        СтрокаВыгрузки = СтрокаВыгрузки + Строка(СтрокаТаблицы[Колонка.Имя]) + ";";
    КонецЦикла;
    
    // Записываем строку в буфер файла
    ЗаписьТекста.ЗаписатьСтроку(СтрокаВыгрузки);
КонецЦикла;

ЗаписьТекста.Закрыть();

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

Решение 2. Выгрузка порциями (партиционирование) и склеивание файлов

Если миллион строк выгрузить в одну огромную таблицу значений, сервер приложений 1С может столкнуться с нехваткой оперативной памяти (особенно в 32-битных рабочих процессах или при жестких лимитах памяти на сервере). Чтобы избежать падения процесса по нехватке памяти, применим стратегию порционной выгрузки.

Мы будем выбирать данные частями (например, по 100 000 строк или по месяцам периода), записывать каждую порцию в отдельный временный файл CSV, а затем склеивать их в один итоговый документ на уровне операционной системы.

Проанализируем пример кода с порционной выгрузкой и использованием встроенного метода ОбъединитьФайлы:


// Коллекция для хранения путей к временным файлам-порциям
МассивВременныхФайлов = Новый Массив;

Для НомерПорции = 1 По 10 Цикл
    // Настраиваем параметры запроса для выборки очередной порции данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 100000 * 
        |ИЗ РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто 
        |ГДЕ Идентификатор > &ПоследнийИдентификатор";
    // ... Установка параметров запроса ...
    
    ТаблицаПорции = Запрос.Выполнить().Выгрузить();
    
    ИмяВременногоФайла = "C:\Temp\part_" + Строка(НомерПорции) + ".csv";
    МассивВременныхФайлов.Добавить(ИмяВременногоФайла);
    
    ЗаписьПорции = Новый ЗаписьТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
    Для Каждого Строка Из ТаблицаПорции Цикл
        // Быстро формируем строку данных
        СтрокаТекста = "";
        Для Каждого Колонка Из ТаблицаПорции.Колонки Цикл
            СтрокаТекста = СтрокаТекста + Строка(Строка[Колонка.Имя]) + ";";
        КонецЦикла;
        ЗаписьПорции.ЗаписатьСтроку(СтрокаТекста);
    КонецЦикла;
    ЗаписьПорции.Закрыть();
КонецЦикла;

// Объединяем полученные файлы в один большой файл
ИтоговыйФайл = "C:\Temp\result_million.csv";
ОбъединитьФайлы(МассивВременныхФайлов, ИтоговыйФайл);

// Удаляем временные файлы-кусочки
Для Каждого ИмяФайла Из МассивВременныхФайлов Цикл
    УдалитьФайлы(ИмяФайла);
    // Или используем встроенную функцию удаления временных файлов
КонецЦикла;

Метод ОбъединитьФайлы работает на уровне платформы крайне эффективно и быстро склеивает бинарные потоки файлов на диске, не нагружая оперативную память сервера.

Решение 3. Сверхбыстрая нативная сериализация JSON через XDTO

Если принимающей стороне не принципиален строгий формат CSV, а нужен структурированный текстовый файл, мы можем использовать нативный механизм сериализации объектов 1С в JSON через XDTO. Этот метод работает на С-уровне платформы 1С и обрабатывает миллионы записей за считанные секунды.

Посмотрим на пример кода, который сериализует выгруженную таблицу значений целиком:


ТаблицаДанных = РезультатЗапроса.Выгрузить();

ЗаписьJSON = Новый ЗаписьJSON;
// Открываем файл для записи JSON
ЗаписьJSON.ОткрытьФайл("C:\Temp\export_data.json", КодировкаТекста.UTF8);

// Нативно записываем всю таблицу одной строчкой без циклов на встроенном языке
СериализаторXDTO.ЗаписатьJSON(ЗаписьJSON, ТаблицаДанных, НазначениеТипаXML.Явное);

ЗаписьJSON.Закрыть();

Это самый производительный способ превратить миллион записей 1С в структурированный текстовый файл штатными средствами платформы. Полученный JSON при необходимости можно легко переформатировать в CSV любым внешним скриптом (например, на Python или Bash).

Решение 4. Использование «ПостроителяОтчета» и «ТабличногоДокумента» без циклов

Еще один профессиональный трюк, позволяющий полностью обойти медленные циклы встроенного языка 1С — использование объекта ПостроительОтчета. Этот объект может напрямую принять таблицу значений в качестве источника данных и мгновенно вывести её в ТабличныйДокумент без ручного обхода строк. Из табличного документа данные сохраняются в текстовый формат одной командой.

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


// Шаг 1. Получаем таблицу данных
ТаблицаДанных = РезультатЗапроса.Выгрузить();

// Шаг 2. Инициализируем построитель отчета
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТаблицаДанных);

// Шаг 3. Выводим данные в Табличный документ (платформенная операция на C++)
ТабДок = Новый ТабличныйДокумент;
Построитель.Вывести(ТабДок);

// Шаг 4. Сохраняем табличный документ в текстовый файл с разделителями-табуляциями (TSV)
ТабДок.Записать("C:\Temp\export_tab.txt", ТипФайлаТабличногоДокумента.TXT);

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

Решение 5. Для SQL-баз: прямое использование утилиты BCP (Bulk Copy Program)

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

Утилита BCP (Bulk Copy Program) — это стандартный инструмент MS SQL для сверхбыстрого экспорта данных. Она способна выгрузить 1 000 000 строк в CSV-файл менее чем за 10 секунд. Запустить выгрузку можно прямо из кода 1С с помощью метода ЗапуститьПриложение:


// Формируем команду для запуска системной утилиты BCP
// Параметры: queryout - выгрузка запроса, -c - символьный формат, -t - разделитель колонок (точка с запятой)
ИмяСервера = "SQL-SERVER-DB";
ИмяБазы = "Enterprise_WorkBase";
ПутьКФайлу = "C:\Temp\bcp_export.csv";

СтрокаЗапуска = "bcp ""SELECT * FROM " + ИмяБазы + ".dbo._AccumRegTotals125"" queryout """ + ПутьКФайлу + """ -c -t"";"" -T -S " + ИмяСервера;

// Запускаем процесс на выполнение и ждем его завершения
КодВозврата = Неопределено;
ЗапуститьПриложение(СтрокаЗапуска, , Истина, КодВозврата);

Если КодВозврата = 0 Тогда
    Сообщить("Выгрузка успешно завершена за несколько секунд!");
Иначе
    Сообщить("Произошла ошибка при выгрузке через BCP.");
КонецЕсли;

Использование этого метода требует знания физической структуры таблиц СУБД (имена таблиц и полей в SQL отличаются от имен метаданных 1С). Однако для регулярных тяжелых интеграций или задач Data Science этот способ является абсолютным лидером по производительности.

Сводная таблица методов выгрузки больших объемов данных

Проанализируем основные методы и выберем оптимальный в зависимости от условий:

  1. Выгрузка результата запроса в ТаблицуЗначений + ЗаписьТекста: Рекомендуется как стандартный, безопасный и кроссплатформенный вариант для баз любого типа (файловых и клиент-серверных). Значительно быстрее обычной выборки.
  2. Выгрузка порциями с использованием ОбъединитьФайлы: Идеально подходит, когда на сервере ограничена оперативная память. Позволяет избежать ошибок "Out of memory" при выгрузке гигантских объемов (от 5-10 миллионов строк).
  3. Сериализация JSON через XDTO: Самый быстрый штатный метод выгрузки «в один клик», не требующий циклов обхода вообще. Рекомендуется, если принимающая система умеет читать формат JSON.
  4. Прямой экспорт через утилиту BCP (SQL): Абсолютный чемпион по скорости. Подходит для администраторов систем и аналитиков данных, имеющих прямой доступ к СУБД.

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

← На главную