Всем привет, коллеги!
В этой статье я поделюсь с вами не только способом ускорения загрузки данных из эксель, но и покажу два новых способа, как элегантно программировать алгоритм загрузки из эксель.
Почему я заменил загрузку из эксель - загрузкой из макета - я написал в свое время статью. Прошу далее считать "загрузку из макета" и "загрузку из эксель" - по своей сути решением одной и той же задачи.
Для макета я использую Табличный документ, в который в пользовательском режиме можно скопировать данные из эксель в макет. Для отладки использую внутренний встроенный макет. Далее код будет одинаковым что для встроенного макета, что для "внешнего" табличного документа.
Как проверить, что описанный ниже способ ускоряет загрузку данных из эксель? Очень просто - запускается отладка и замер производительности "До" и "После" изменений алгоритма.
Скажем так, с замера производительности и началось улучшение процесса (алгоритма) загрузки. После очередных замеров был выявлен код, который наиболее долго выполнялся. В моем примере, долго выполнялся код получения Контрагента по реквизиту "Номер в реестре".
Если НомерКолонки = 3 Тогда
ЗначениеЯчейки = Справочники.Контрагенты.НайтиПоРеквизиту( "НомерВРеестре", Стр.НомерВРеестре);
КонецЕсли;
Увидев длительное выполнение кода для 3000 контрагентов, решил потестить другой способ, который оправдал надежды. Предварительно задаю соответствие контрагентов по номерам в реестре.
СоответствиеКонтрагентов = Новый Соответствие;
Выборка = Справочники.Контрагенты.Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.НомерВРеестре>0 Тогда
СоответствиеКонтрагентов.Вставить( Выборка.НомерВРеестре,
Выборка.Ссылка);
КонецЕсли;
КонецЦикла;
Сам код цикла после отладки я записываю в одну строку - платформа быстрее обрабатывает цикл, записанный в одну строку. В этом я вижу особый стиль программирования - проверенные и простые алгоритмы записывать в одну строку, поэтому часто использую.
Далее, в процессе обхода строк эксель, получаю Контрагента весьма элегантно и очень быстро. Хочу заметить, что в целом алгоритм заработал быстрее - не только кусок получения контрагента по номеру в реестре, а в целом с учетом инициализации соответствия и далее потом с учетом обхода строк эксель.
Если НомерКолонки = 3 Тогда
ЗначениеЯчейки = СоответствиеКонтрагентов.Получить(
Стр.НомерВРеестре);
КонецЕсли;
Теперь, добавлю пару элегантных ноу-хау.
1. Эксель предоставили такой, что строки в нем могли задваиваться и затраиваться по ряду ключевых полей. Поэтому необходимо было проверять строки по ключевым полям. В случае повторения ключевых полей эти строки надо пропускать и не обрабатывать.
Если бы ключевое поле было одно или даже два, я бы по старинке делал сравнение с предыдущим значением поля и т.д.
Но тут оказалось 4 ключевых поля, и мне стало интересно попробовать другой способ. Как раз недавно читал про шифрование данных, поэтому родилась идея создать КлючСтроки для каждой строки эксель.
МассивСтрок = Новый Массив;
Для НомерСтроки = 2 По Макет.ВысотаТаблицы Цикл
//сначала проверим на задвоенность
КлючСтроки = СокрЛП(Макет.Область( НомерСтроки, 1
,НомерСтроки, 1).Текст) //номер в реестре
+ СокрЛП(Макет.Область( НомерСтроки,7,
НомерСтроки, 7).Текст) //состояние
+ СокрЛП(Макет.Область( НомерСтроки, 8,
НомерСтроки, 8).Текст) //номер служебный
+ СокрЛП(Макет.Область( НомерСтроки, 9,
НомерСтроки, 9).Текст); //дата
//проверим на новую запись или дубль
Если МассивСтрок.Найти(КлючСтроки) = Неопределено Тогда
МассивСтрок.Добавить(КлючСтроки);
Иначе
Продолжить;
КонецЕсли;
//обработка строк в цикле
....
КонецЦикла;
После отладки я записываю конструкцию создания Ключа строки и конструкцию проверки на условие "Если" в одну строку. Платформа 1С быстрее производит конкатенацию строк и проверку условия "Если", записанных в одну строку. Поэтому я делаю часто, и уже считаю хорошим стилем программирования писать проверенные и простые алгоритмы в одну строку.
2. Второй ноу-хау следующий - я загружаю поля эксель в соответствующие поля табличной части обработки. Далее в обработке провожу необходимые расчеты. И вот раньше я каждое поле получал как
СтрТаблЧасти.НомерВРеестре = СокрЛП(Макет.Область( НомерСтроки, 1, НомерСтроки, 1).Текст)
Так как полей оказалось много, то я решил использовать другой способ чтения и записи значений полей. Сначала создал структуру для хранения имен полей, далее использую ее для компактного заполнения строк табличной части контрагентов.
СписокПолей = "НомерВРеестре, ИНН, Контрагент, Поле4, Поле5, Поле6, Поле7, НомерСлужебный, Дата, Поле10, Поле11, Поле12, Поле13, Поле14, Поле15, Поле16, Поле17";
МассивПолей = СтрРазделить(СписокПолей, ","); //задаем массив полей
СоответствиеПолей = Новый Соответствие; //задаем соответствие полей
СоответствиеПолей.Вставить("НомерВРеестре", 1);
СоответствиеПолей.Вставить("ИНН", 2);
СоответствиеПолей.Вставить("Контрагент", 3);
СоответствиеПолей.Вставить("Поле4", 4);
СоответствиеПолей.Вставить("Поле5", 5);
СоответствиеПолей.Вставить("Поле6", 6);
СоответствиеПолей.Вставить("Поле7", 7);
СоответствиеПолей.Вставить("НомерСлужебный", 8);
СоответствиеПолей.Вставить("Дата", 9);
СоответствиеПолей.Вставить("Поле10", 10);
СоответствиеПолей.Вставить("Поле11", 11);
СоответствиеПолей.Вставить("Поле12", 12);
СоответствиеПолей.Вставить("Поле13", 13);
СоответствиеПолей.Вставить("Поле14", 14);
СоответствиеПолей.Вставить("Поле15", 15);
СоответствиеПолей.Вставить("Поле16", 16);
СоответствиеПолей.Вставить("Поле17", 17);
После создания структуры полей, становится проще и компактнее обращаться к полям.
Для НомерСтроки = 1 По Макет.ВысотаТаблицы Цикл
//...проверка на задвоенность
//если не задвоено, то создадим запись
Стр = СписокКонтрагентов.Добавить();
Для Каждого Ключ Из МассивПолей Цикл
Поле = СокрЛП(Ключ);
НомерКолонки = СоответствиеПолей[Поле];
ЗначениеЯчейки = СокрЛП(Макет.Область( НомерСтроки, НомерКолонки, НомерСтроки, НомерКолонки).Текст);
Если ПустаяСтрока(ЗначениеЯчейки) Тогда Продолжить; КонецЕсли;
Если НомерКолонки = 3 Тогда ЗначениеЯчейки = СоответствиеКонтрагентов.Получить(Стр.НомерВРеестре);
ИначеЕсли НомерКолонки = 12 ИЛИ НомерКолонки = 13 ИЛИ НомерКолонки = 14 Тогда ЗначениеЯчейки = Булево(Число(ЗначениеЯчейки));
ИначеЕсли НомерКолонки = 6 ИЛИ НомерКолонки = 9 ИЛИ НомерКолонки = 10 Тогда ЗначениеЯчейки = ДатаИзСтроки(ЗначениеЯчейки);
КонецЕсли;
Стр[Поле] = ЗначениеЯчейки;
КонецЦикла;
КонецЦикла;
Собственно, это все - более компактный код позволяет использовать модульность, компактность кода, повторяемость кода для других эксель. Подобный подход я использовал для трех разных эксель для загрузки в одну обработку.
На этом все. Всем добра!
С пользой для клиентов, Рустем