Как ускорить выгрузку данных из выборки запроса или таблицы значений в текстовый файл в 1С?

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

При выгрузке больших массивов данных (десятки и сотни тысяч строк) из 1С в текстовые форматы (CSV, TXT) разработчики часто сталкиваются со значительным падением производительности. Запрос к базе данных может выполняться за 1–2 секунды, однако последующий обход выборки и формирование файла занимает несколько минут. Проанализируем ключевые причины замедления и разберем эффективные способы оптимизации этого процесса.

Причины медленной выгрузки данных в цикле

Выясним основные причины, из-за которых обработка данных во встроенном языке 1С может занимать непропорционально много времени:

  1. Обращение к реквизитам через точку в цикле обхода. Конструкции вида Выборка.Номенклатура.ПолноеНаименование или вызовы свойств ссылочных объектов внутри цикла провоцируют дополнительные неявные запросы к базе данных на каждой итерации. При 40 000 строк это приводит к 40 000 дополнительных обращений к СУБД.
  2. Выполнение логических и арифметических операций во встроенном языке. Поиск подстрок, вычисление условий и проверка значений в цикле нагружают один поток процессора вместо выполнения этой работы на стороне базы данных.
  3. Неэффективная конкатенация строк. Строковые объекты во встроенном языке 1С является неизменяемыми (immutable). При постоянном сложении строк (Стр = Стр + Символ + Значение) платформа регулярно выделяет новые блоки оперативной памяти, что приводит к ее фрагментации и дополнительной нагрузке на сборщик мусора.
  4. Использование неоптимальных объектов работы с текстом. Объект ТекстовыйДокумент при вызове метода ДобавитьСтроку() расходует больше ресурсов на внутреннюю структуризацию данных, чем специализированный объект ЗаписьТекста.

Шаг 1. Перенос логики, представлений и вычислений в запрос

Рассмотрим основной подход к ускорению — максимальное вычисление данных на стороне СУБД с помощью языка запросов 1С.

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


Пока Выборка.Следующий() Цикл
    Стр = "" + Выборка.ШтрихКод;
    Стр = Стр + Символ(166) + Выборка.Номенклатура; // Ссылочный тип
    
    // Проблема: обращение через точку в цикле провоцирует запрос к БД
    ПроверкаПодарка = СтрНайти(СокрЛП(Выборка.Номенклатура.ПолноеНаименование), "ПОДАРКИ") > 0;
    
    Если ПроверкаПодарка Тогда
        Цена = 0;
    Иначе
        Цена = Формат(Выборка.РозничнаяЦена, "ЧГ=");
    КонецЕсли;
    
    ТекстДок.ДобавитьСтроку(Стр);
КонецЦикла;

Чтобы избавиться от накладных расходов, перенесем получение текстовых представлений ссылок и проверку условий в текст запроса. Воспользуемся функциями ПРЕДСТАВЛЕНИЕ() и оператором ВЫБОР КОГДА ... ИНАЧЕ ... КОНЕЦ:


ВЫБРАТЬ
    Товары.ШтрихКод КАК ШтрихКод,
    ПРЕДСТАВЛЕНИЕ(Товары.Номенклатура) КАК НоменклатураПредставление,
    ПРЕДСТАВЛЕНИЕ(Товары.Производитель) КАК ПроизводительПредставление,
    ВЫБОР
        КОГДА Товары.Номенклатура.ПолноеНаименование ПОДОБНО "%ПОДАРКИ, ТЕСТЕРЫ, ОБОРУДОВАНИЕ%"
            ТОГДА 0
        ИНАЧЕ Товары.РозничнаяЦена
    КОНЕЦ КАК РозничнаяЦенаВычисленная,
    ВЫБОР
        КОГДА Товары.Объем * 1000000 > 0.0001
            ТОГДА Товары.Объем * 1000000
        ИНАЧЕ 1
    КОНЕЦ КАК ОбъемВычисленный
ИЗ
    РегистрСведений.Товары КАК Товары

Результат: Замена обращения к реквизитам через точку на функцию ПРЕДСТАВЛЕНИЕ() и перенос условных выражений в язык запросов сокращают время выгрузки в разы (в тестируемом примере — со 160 до 25-30 секунд).

Шаг 2. Оптимизация работы с текстовым файлом и памятью

Разберем эффективные приемы работы с файловыми объектами и строками во встроенном языке:

  1. Замена ТекстовыйДокумент на ЗаписьТекста. Объект ЗаписьТекста осуществляет потоковую буферизованную запись непосредственно в файл, исключая лишние накладные расходы на хранение документа в оперативной памяти.
  2. Вынос повторяющихся выражений из цикла. Вычислим разделитель строк заранее, чтобы платформа не тратила ресурсы на вызов функции Символ() на каждой итерации:

СимволРазделитель = Символ(166);
ФайлНоменклатура = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.UTF8);

Пока Выборка.Следующий() Цикл
    // Сборка строки с заранее объявленным разделителем
    Стр = Выборка.ШтрихКод 
        + СимволРазделитель + Выборка.НоменклатураПредставление
        + СимволРазделитель + Выборка.ПроизводительПредставление
        + СимволРазделитель + Формат(Выборка.РозничнаяЦенаВычисленная, "ЧГ=");
        
    ФайлНоменклатура.ЗаписатьСтроку(Стр);
КонецЦикла;

ФайлНоменклатура.Закрыть();

Альтернативные методы ускорения формирования файлов

Рассмотрим дополнительные техники платформы 1С для оптимизации выгрузки данных:

1. Использование массива и функции СтрСоединить()

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


СимволРазделитель = Символ(166);
МассивКолонок = Новый Массив;

Пока Выборка.Следующий() Цикл
    МассивКолонок.Очистить();
    МассивКолонок.Добавить(Выборка.ШтрихКод);
    МассивКолонок.Добавить(Выборка.НоменклатураПредставление);
    МассивКолонок.Добавить(Формат(Выборка.РозничнаяЦенаВычисленная, "ЧГ="));
    
    Стр = СтрСоединить(МассивКолонок, СимволРазделитель);
    ФайлНоменклатура.ЗаписатьСтроку(Стр);
КонецЦикла;

2. Выгрузка через ТабличныйДокумент

Платформа 1С содержит встроенный быстрый механизм сохранения табличных документов в формат CSV. Запись выполняется средствами ядра платформы C++ и обрабатывает данные быстрее обхода во встроенном языке:


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

// Быстрая выгрузка нативным методом платформы
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.CSV);

3. Буферизация и параллельная обработка

При выгрузке сверхбольших объемов данных (миллионы строк) рекомендуется разделить выборку на интервалы (например, по коду или УИД) и запустить несколько фоновых заданий через объект ФоновыеЗадания. Каждое задание параллельно выгружает свою часть данных во временный файл, после чего итоговые файлы объединяются.

Применяя перенос вычислений в язык запросов и используя объект ЗаписьТекста, удается сократить время формирования файла в разы и обеспечить стабильную работу сервера.

← На главную