Загрузка из эксель (распараллеливание записи при работе с большими файлами)

21.04.25

Интеграция - Загрузка и выгрузка в Excel

Загрузка объемных данных из эксель файлов путем распараллеливания записи.

Регулярно возникают задачи по парсингу файлов эксель. Тема хорошо изучена, однако с некоторой периодичностью возникают нюансы и приходится совершенствовать базовый механизм. В моем случае возникла необходимость закачать в регистр сведений много объемных файлов (~ 1 млн. строк). На загрузку одного файла существующим алгоритмом у меня уходило неприлично большое время - несколько часов. Пришла идея распараллелить запись, путем формирования фоновых заданий, каждое из которых сбрасывает свою порцию данных. В итоге, скорость закачки существенно увеличилась - на один файл время загрузки составило 20-30 мин.

(Кстати, в ряде случаев, существенно помогла и программная оптимизация файла - программное удаление пустых столбцов, которые 1С воспринимала как значимые.)

 

1. Был создан регистр сведений, структура измерений которого совпадает со структурой столбцов исходного файла (чтобы не описывать явным образом типизацию столбцов ТЗ):

ДанныеИзФайла = РегистрыСведений.А.СоздатьНаборЗаписей().Выгрузить();

 

2. Собственно открытие, оптимизация и чтение данных файла эксель:

Попытка
        Excel        = Новый COMОбъект("Excel.Application");        
Исключение 
        Excel = Неопределено;
        Возврат;
КонецПопытки; 
    
Excel.FileValidation = 1;
Попытка
        РабочаяКнига = Excel.WorkBooks;    
        РабочаяКнига.Open(СокрЛП(ИмяФайла));
Исключение 
        Excel = Неопределено;        
        Возврат;
КонецПопытки;                                                  
    
КоличествоЛистов             = Excel.WorkSheets.Count();
Лист                         = Excel.WorkSheets(1);
ИспользуемыйЛист             = Лист.UsedRange;        
КоличествоЯчеек              = Excel.Worksheets(1).Cells(1,1).SpecialCells(11).Row;
ВсегоКолонок = Лист.Cells(1,1).SpecialCells(11).Column;
ВсегоСтрок = Лист.Cells(1,1).SpecialCells(11).Row;
Область = Лист.Range(Лист.Cells(1,1), Лист.Cells(ВсегоСтрок,ВсегоКолонок));

  

//удаление заведомо неиспользуемых столбцов (за НомерМаксимальнойКолонки)

Если НомерМаксимальнойКолонки < ВсегоКолонок Тогда
        ОбластьПустых = Лист.Range(Лист.Cells(1,НомерМаксимальнойКолонки+1), Лист.Cells(ВсегоСтрок,ВсегоКолонок));
        ОбластьПустых.Delete(-4159);
КонецЕсли;    
                
Попытка
        Данные = Область.Value.Выгрузить();
        Excel.ActiveWorkbook.Close(Ложь);            
Исключение
        Excel = Неопределено;
        Возврат;
КонецПопытки;        
            
Попытка
        Excel.QUIT();
        Excel        = Неопределено;
Исключение
        Excel = Неопределено;
        Возврат;
КонецПопытки;

3. Цикл обработки считанных данных:

СчетчикСбросаДанныхВКэш = 0;
ДанныеИзФайлаКоличество = 0;

    
ВсегоСтрок = Данные[0].Количество();
                
Для ТекНомерСтрокиФайла = Ексел_НомерСтрокиНачало-1 По ВсегоСтрок-1 Цикл        
        Если СчетчикСбросаДанныхВКэш = ЧислоЗаписейДляСброса Тогда
            ВызватьСопоставитьНаСервере_ДобавитьВРС(ДатаДанных, ДанныеИзФайла, НомерПервойСтрокиПорции,МассивКлючейФоновыхЗаданий);
            ДанныеИзФайлаКоличество = ДанныеИзФайлаКоличество + ДанныеИзФайла.Количество();
            ДанныеИзФайла.Очистить();
            СчетчикСбросаДанныхВКэш = 0;
                
            НомерПервойСтрокиПорции = НомерПервойСтрокиПорции + ЧислоЗаписейДляСброса;

        КонецЕсли;
        СчетчикСбросаДанныхВКэш = СчетчикСбросаДанныхВКэш + 1;    
            
        НовСтр = ДанныеИзФайла.Добавить();         
        Для j = 0 По ВсегоКолонок - 1 Цикл
            Если j = ВсегоКолонок - 1 Тогда  //запись ресурса РС, в моем случае Количество
                НовСтр.Количество = Данные[j][ТекНомерСтрокиФайла];
            Иначе    
                НовСтр[ДанныеИзФайла.Колонки[j+1].Имя] = Данные[j][ТекНомерСтрокиФайла];
            КонецЕсли;    
        КонецЦикла;          
    КонецЦикла;    

4. Финальный сброс данных    

    

ВызватьСопоставитьНаСервере_ДобавитьВРС(ДатаДанных, ДанныеИзФайла, НомерПервойСтрокиПорции,МассивКлючейФоновыхЗаданий);
    ДанныеИзФайлаКоличество = ДанныеИзФайлаКоличество + ДанныеИзФайла.Количество();
            
    //ждем выполнения всех фоновых, с ним связанных
    НеВсеВыполнены = Истина;
    Пока НеВсеВыполнены Цикл 
        _ОбщиеФункции.Задержка(20);
        МассивФоновыхЗаданий = ФоновыеЗадания.ПолучитьФоновыеЗадания();
        Для Каждого Эл Из МассивФоновыхЗаданий Цикл       
            Индекс_ = МассивКлючейФоновыхЗаданий.Найти(Эл.Ключ);
            Если Индекс_ <> Неопределено И Эл.Состояние <> СостояниеФоновогоЗадания.Активно Тогда
                МассивКлючейФоновыхЗаданий.Удалить(Индекс_);
            КонецЕсли;    
            НеВсеВыполнены = МассивКлючейФоновыхЗаданий.Количество() > 0;
        КонецЦикла;    
    КонецЦикла;


 

Jтдельная процедура по формированию и вызову фонового задания для сброса данных:

&НаСервере
Процедура ВызватьСопоставитьНаСервере_ДобавитьВРС(ДатаОтбора,ТЗ_Загруженная,НачальныйНомерСтроки,МассивКлючейФоновыхЗаданий)    
    
    Структура = Новый Структура;
    Структура.Вставить("ДатаОтбора", ДатаОтбора);
    Структура.Вставить("ТЗ_Загруженная", ТЗ_Загруженная);
    Структура.Вставить("НачальныйНомерСтроки", НачальныйНомерСтроки);
    ПараметрыВыполнения = Новый Массив;
    ПараметрыВыполнения.Добавить(Структура);                               
    КлючФоновогоЗадания = "_"+Формат(ДатаОтбора,"ДФ=dd.MM.yy")+"_"+НачальныйНомерСтроки+"_/"+Формат(ТекущаяДата(),"ДФ=с.мс");
    МассивКлючейФоновыхЗаданий.Добавить(КлючФоновогоЗадания);
    ФоновыеЗадания.Выполнить("_ОбщиеФункции.СопоставитьНаСервере_ДобавитьВРС",ПараметрыВыполнения,КлючФоновогоЗадания,КлючФоновогоЗадания);
        
КонецПроцедуры

 

В моем случае _ОбщиеФункции - это общий модуль со свойствами "Сервер", "Вызов сервера".
 

Процедура СопоставитьНаСервере_ДобавитьВРС(Параметры) Экспорт
    
    ДатаОтбора                 = Параметры.ДатаОтбора;
    ТЗ_Загруженная             = Параметры.ТЗ_Загруженная;    
    НачальныйНомерСтроки     = Параметры.НачальныйНомерСтроки;
    ЗаписыватьИзменения     = Истина;    //ЗначениеЗаполнено(ИмяФайла);
    НомерСтроки             = НачальныйНомерСтроки;
    
    Если ТЗ_Загруженная.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;    
    
    Колонки = ТЗ_Загруженная.Колонки;
    
    Для Каждого Стр Из ТЗ_Загруженная Цикл
        
        НомерСтроки = НомерСтроки + 1;
                                   
        НовЗапись = РегистрыСведений.А.СоздатьМенеджерЗаписи();
        Для Каждого Кол Из Колонки Цикл
            НовЗапись[Кол.Имя] = Стр[Кол.Имя];        
        КонецЦикла;
        

        НовЗапись.Записать(Истина);                  
                    
    КонецЦикла;  
        
КонецПроцедуры

В итоге получается компактное и быстро прикручиваемое решение по считыванию больших файлов. В моменте число фоновых заданий может достигать числа 130-150, каждое из которых работает около 2 минут. Для себя эмпирически установил размер сбрасываемой порции в 3000 строк.

Вступайте в нашу телеграмм-группу Инфостарт

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Загрузка и выгрузка в Excel Маркетплейсы Программист Бухгалтер Пользователь 1С:Предприятие 8 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Бухгалтерский учет Управленческий учет Платные (руб)

Реальный помощник, с помощью которого Вы преобразуете необходимые документы для Wildberries, OZON, ЯндексМаркет, ЛаМода, Мегамаркет, Aliexpress, Детский мир, Магнит Маркет (быв.МагнитЭкспресс), Лемана про, ЭНФАНТА (Акушерство), Летуаль, Твой дом, Золотое Яблоко, Каспи, Авито, Аптеки+, Тинькоф в документы "Отчет комиссионера (агента) о продажах" и другие. Работает в 1С:БП 3.0, 1С:БП 3.0 КОРП, 1С:УТ 11, 1С:УНФ, 1С:ERP.

5490 руб.

12.08.2021    47453    615    71    

223

Загрузка и выгрузка в Excel Розничная торговля Логистика, склад и ТМЦ Ценообразование, анализ цен Прайсы Системный администратор Программист 1С:Предприятие 8 1С:Комплексная автоматизация 1.х 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Управленческий учет Платные (руб)

Загрузка номенклатуры из файлов Excel (xls, xlsx, ods, csv, mxl) в УТ11, КА 2, ERP 2, Розница 2. Задействованы все возможности конфигурации - заполнение реквизитов номенклатуры, дополнительных реквизитов и сведений, характеристики, доп.реквизиты и сведения характеристик. Дополнительные обработки для расширения возможностей.

12444 руб.

29.10.2014    235312    763    543    

504

Маркетплейсы Загрузка и выгрузка в Excel Программист Пользователь 1С:Предприятие 8 1С:Управление торговлей 10 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Управленческий учет Платные (руб)

Загрузка данных отчета о реализации и продаже товаров из сервиса Яндекс.Маркета «Беру» из отчетов Excel для конфигурации: Бухгалтерия предприятия, редакция 3.0; Управление торговлей, редакция 11; Управление торговлей, редакция 10.3; Управление нашей фирмой, редакция 3.0 и Розница, редакция 3.0 в документ «Отчет комиссионера о продажах».

6000 руб.

09.12.2020    30673    304    1    

151

Загрузка и выгрузка в Excel Программист Бухгалтер Пользователь 1С:Предприятие 8 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Розничная и сетевая торговля (FMCG) Управленческий учет Платные (руб)

Загрузка данных отчета о реализации товаров из сервиса Lamoda «Ламода» для конфигурации: 1С:Бухгалтерия предприятия, редакция 3.0; 1C:Управление торговлей, редакция 11 и 1C:Управление нашей фирмой, редакция 3.0 в документ "Отчет комиссионера (агента) о продажах".

6000 руб.

01.03.2021    12930    77    0    

34
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. fuser 3 29.04.25 06:10 Сейчас в теме
А не быстрее загружать целиком?
ТабличныйДокумент.Прочитать(ФайлEXCEL, СпособЧтенияЗначенийТабличногоДокумента.Значение);
2. ko-@ 2 05.05.25 11:00 Сейчас в теме
(1) У меня стояла задача перенести из прочитанного файла (уже из таблицы значений) данные в регистр сведений. Без распараллеливания этот процесс занимает крайне много времени. Собственно, использование фоновых заданий, каждое из которых пишет в регистр свою порцию в десятки раз сокращает время обработки.

Сам контекст задачи был таков, что исходный файл эксель мог содержать как избыточные данные, так и требующие предварительной обработки (преобразование значений). Поэтому требуется как предварительная обработка файла эксель средствами VBA, так и построчная проверка значений. Я использовал метод

Данные = Область.Value.Выгрузить();

который на выходе дает массив массивов, время исполнения совсем невелико, тут не стояла задача оптимизации. Если дойдут руки, попробую сравнить временные затраты между этим механизмом и Вашим предолжением.
Для отправки сообщения требуется регистрация/авторизация