Быстрая функция чтения данных с листа Excel




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2026-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

99 Comments

  1. alexk-is

    Может быть текст функции раскрасить?

    Reply ↓
  2. wildhog

    (1) Хорошая идея, твоя обработка замечательно справиться 🙂

    Reply ↓
  3. СергейКа

    Век живи — век учись… Даже не подозревал о возможности Область.Value.Выгрузить();

    Reply ↓
  4. wildhog

    Область.Value — это ComSafeArray. О нем в конфигураторе довольно подробно написано + методы тоже имеются.

    Reply ↓
  5. artem666

    быстрая функция это ADO

    Reply ↓
  6. A'Huli

    Просто и со вкусом.

    Reply ↓
  7. kadr
    Reply ↓
  8. acsent

    ADO не всегда можно. Например нельзя когда разного типа данные в одном столбце. Привоодятся к строке

    Reply ↓
  9. Душелов

    (7) Попробуй еще протестить на скорость http://infostart.ru/projects/3214/

    Reply ↓
  10. kadr

    (9) Дык а чего там тестить?

    Только оптимальную реализацию алгоритмов работы с Compound объектами?

    Reply ↓
  11. kadr

    (9) На правах рекламы :)))

    Ваша реализация, ИМХО, самая быстрая, хотя не тестил 🙂

    Reply ↓
  12. kadr

    (9) Болт. не самая быстрая

    Код

    Скрипт = Новый COMОбъект(«MSScriptControl.ScriptControl»);

    Скрипт.language = «javascript»;

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    ИмяВК=»AddIn.ExcelEditor»;

    Попытка

    ПодключитьВнешнююКомпоненту(ИмяВК);

    Исключение

    Предупреждение(«Не удалось подключить компоненту » + ИмяВК);

    КонецПопытки;

    Попытка

    Экзель = Новый(ИмяВК);

    Исключение

    Предупреждение(«Ошибка создания объекта внешней компоненты: » + ИмяВК);

    КонецПопытки;

    Если Экзель.ОткрытьФайл(«c:delado.xls») тогда

    Если Экзель.ОткрытьЛист(1) тогда

    КоличествоКолонок = Экзель.ПолучитьКоличествоКолонок();

    КоличествоСтрок = Экзель.ПолучитьКоличествоСтрок();

    Таблица = Новый ТаблицаЗначений;

    Для Сч = 1 по КоличествоКолонок Цикл

    Таблица.Колонки.Добавить(«Колонка»+Строка(Сч));

    КонецЦикла;

    Для Сч = 1 по КоличествоСтрок Цикл

    СтрокаТЗ = Таблица.Добавить();

    Для Сч1 = 1 по КоличествоКолонок Цикл

    СтрокаТЗ[Сч1-1] = Экзель.ПолучитьЗначениеЯчейки(Сч, Сч1);

    КонецЦикла;

    КонецЦикла;

    Экзель.Выполнено();

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    ОчиститьСообщения();

    Сообщить(«Выполнение скрипта заняло: » + Строка(ВремяОкончания — ВремяНачала) + » мсек. «);

    КонецЕсли;

    КонецЕсли;

    Результат

    Выполнение скрипта заняло: 2125 мсек.

    Reply ↓
  13. Душелов

    (12) А где

    «Строк = 100;

    Колонок = 100;»

    ?

    Reply ↓
  14. Душелов

    (12) И как бы создание объектов стоит убрать из тестов, а оставить только чтение данных.

    Reply ↓
  15. kadr

    Это был тест чтения

    Обработано строк: 8 колонок: 10000 итого ячеек; 80000

    Тест записи

    Выполнение скрипта заняло: 484 мсек.

    КОД

    Скрипт = Новый COMОбъект(«MSScriptControl.ScriptControl»);

    Скрипт.language = «javascript»;

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    ИмяВК=»AddIn.ExcelEditor»;

    Попытка

    ПодключитьВнешнююКомпоненту(ИмяВК);

    Исключение

    Предупреждение(«Не удалось подключить компоненту » + ИмяВК);

    КонецПопытки;

    Попытка

    Экзель = Новый(ИмяВК);

    Исключение

    Предупреждение(«Ошибка создания объекта внешней компоненты: » + ИмяВК);

    КонецПопытки;

    Если Экзель.ОткрытьФайл(«c:delado.xls») тогда

    Если Экзель.ОткрытьЛист(2) тогда

    Строк = 100;

    Колонок = 100;

    Для Сч = 1 по Строк Цикл

    Для Сч1 = 1 по Колонок Цикл

    Экзель.РедактироватьЗначениеЯчейки(0, 1, Сч*Сч1);

    КонецЦикла;

    КонецЦикла;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    ОчиститьСообщения();

    Сообщить(«Выполнение скрипта заняло: » + Строка(ВремяОкончания — ВремяНачала) + » мсек. «);

    Экзель.Выполнено();

    КонецЕсли;

    КонецЕсли;

    Кстати параметры функции РедактироватьЗначениеЯчейки в документации не указаны 🙂

    Reply ↓
  16. Душелов

    (15) Попытка/Исключение хорошо кушают милисекунды, кстати.

    Reply ↓
  17. kadr

    Создание объектов включил специально, чтобы увидеть скорость выполнения не именно операция ввода-вывода, а скорость импорта-экспорта

    Коды тестов есть — дорабатывать и оптимизировать можно всегда 🙂

    Reply ↓
  18. kadr

    Ваш код кстати 🙂

    Reply ↓
  19. Душелов

    (18) Пожертвовать скоростью с попыткой/исключение при открытии обработки можно… Важна скорость обработки многомегобайтных файлов.

    Reply ↓
  20. kadr

    Ок без Попытка-исключение

    Чтение

    Выполнение скрипта заняло: 2093 мсек.

    Запись

    Выполнение скрипта заняло: 469 мсек.

    Reply ↓
  21. Душелов

    И попробуй сделать 80000 строк.

    Reply ↓
  22. Душелов

    И попробуй сделать 3 колонки — число, строка и дата 😉

    Просто самому интересны результаты.

    Reply ↓
  23. kadr

    Без создания объектов чисто операции I/O

    ЧТЕНИЕ

    wildhog

    КОД

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Область = ЛистЭксель.Range(ЛистЭксель.Cells(1,1), ЛистЭксель.Cells(ВсегоСтрок,ВсегоКолонок));

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

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    результат

    Выполнение скрипта заняло: 109 мсек.

    Обработано строк: 8 колонок: 10000 итого ячеек; 80000

    ADO

    КОД

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    ТЗ = ADOUtils.ADORecordsetToValueTable(НаборДанных);

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Результат

    Выполнение скрипта заняло: 359 мсек.

    Обработано строк: 8 колонок: 10000 итого ячеек; 80000

    Использовалась ВК GameWithFire

    Душелов

    КОД

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Для Сч = 1 по КоличествоСтрок Цикл

    СтрокаТЗ = Таблица.Добавить();

    Для Сч1 = 1 по КоличествоКолонок Цикл

    СтрокаТЗ[Сч1-1] = Экзель.ПолучитьЗначениеЯчейки(Сч, Сч1);

    КонецЦикла;

    КонецЦикла;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Результат

    Выполнение скрипта заняло: 1843 мсек.

    ЗАПИСЬ

    COMSAfeArray

    КОД

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Лист.Range(Лист.Cells(1,1),Лист.cells(Строк,Колонок)).Value = Массив2;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Результат

    Выполнение скрипта заняло: 47 мсек.

    Построчно

    КОД

    Строк = 100;

    Колонок = 100;

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Для Сч = 1 по Строк Цикл

    Для Сч1 = 1 по Колонок Цикл

    Лист.cells(Сч,Сч1).Value = сч1;

    КонецЦикла;

    КонецЦикла;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Результат

    Выполнение скрипта заняло: 22500 мсек.

    Душелов

    КОД

    Строк = 100;

    Колонок = 100;

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Для Сч = 1 по Строк Цикл

    Для Сч1 = 1 по Колонок Цикл

    Экзель.РедактироватьЗначениеЯчейки(0, 1, Сч*Сч1);

    КонецЦикла;

    КонецЦикла;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Результат

    Выполнение скрипта заняло: 219 мсек.

    Reply ↓
  24. Душелов

    Кинь файлик мне 😉 Посмотрю, что да как… Что-то долго слишком.

    Reply ↓
  25. Душелов

    (23) На счет чтение… Там везде ТЗ 1С-овское получается?

    А то у автора:

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

    Для Счетчик = 0 По ВсегоКолонок-1 Цикл

    ТЗ.ЗагрузитьКолонку(Данные[Счетчик], Счетчик);

    КонецЦикла;»

    Как бы не объективно получается 😉

    Reply ↓
  26. kadr

    (19) согласен

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

    Думаю косяк в многократном вызове метода ВК РедактироватьЗначениеЯчейки, а там по цепочке callAsproc, callasfunc затем у тебя создание объекта типа cell и тд

    (22)

    Сегодня уже не смогу протестить с разными параметрами

    (24) какой файлик? Excel? или тесты? или то и другое? мыло?

    Reply ↓
  27. Душелов

    И на счет записи тоже самое. Приведи запись из 1С-овского ТЗ в 1 варианте.

    Reply ↓
  28. Душелов

    (26) Эти тесты пока не объективны 😉

    Reply ↓
  29. kadr

    (27)

    Эксель = Новый COMОбъект(«Excel.Application»);

    Книга = Эксель.Workbooks.Add(«c:delado.xls»);

    Лист = Книга.Sheets(2);

    Скрипт = Новый COMОбъект(«MSScriptControl.ScriptControl»);

    Скрипт.language = «javascript»;

    //ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Строк = 100;

    Колонок = 100;

    Массив = Новый Массив(Строк,Колонок);

    Для Сч = 0 по Строк — 1 Цикл

    Для Сч1 = 0 по Колонок — 1 Цикл

    Массив[сч][сч1] = сч1;

    КонецЦикла;

    КонецЦикла;

    Массив2 = Новый COMSafeArray(Массив, «VT_I4»,Строк,Колонок);

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Лист.Range(Лист.Cells(1,1),Лист.cells(Строк,Колонок)).Value = Массив2;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Сообщить(«Выполнение скрипта заняло: » + Строка(ВремяОкончания — ВремяНачала) + » мсек. «);

    Эксель.Visible = 1;

    Вот из ComSafeArray/ надо из Тз — засунь ТЗ в массив и выведи

    Согласен, что не все 1С-совское, но согласись, мы замеряем не скорость работы какого-то определенного интерфейса, а скорость операции импорта-экспорта

    (24)

    А ты реализцй у с(27)

    Эксель = Новый COMОбъект(«Excel.Application»);

    Книга = Эксель.Workbooks.Add(«c:delado.xls»);

    Лист = Книга.Sheets(2);

    Скрипт = Новый COMОбъект(«MSScriptControl.ScriptControl»);

    Скрипт.language = «javascript»;

    //ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Строк = 100;

    Колонок = 100;

    Массив = Новый Массив(Строк,Колонок);

    Для Сч = 0 по Строк — 1 Цикл

    Для Сч1 = 0 по Колонок — 1 Цикл

    Массив[сч][сч1] = сч1;

    КонецЦикла;

    КонецЦикла;

    Массив2 = Новый COMSafeArray(Массив, «VT_I4»,Строк,Колонок);

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Лист.Range(Лист.Cells(1,1),Лист.cells(Строк,Колонок)).Value = Массив2;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Сообщить(«Выполнение скрипта заняло: » + Строка(ВремяОкончания — ВремяНачала) + » мсек. «);

    Эксель.Visible = 1;

    Вот из ComSafeArray/ надо из Тз — засунь ТЗ в массив и выведи

    Согласен, что не все 1С-совское, но согласись, мы замеряем не скорость работы какого-то определенного интерфейса, а скорость операции импорта-экспорта

    Ты реализуй в своей вк метод типа ТЗВЭкзель 🙂

    Reply ↓
  30. wildhog

    Решил и я потестировать оба варианта.

    Методика тестирования.

    Создал 3 файла, в каждом 6 колонок, строк в 1м файле — 12000, 2м ~ 24000, 3м ~ 49000.

    1 тест. запуск последовательно (сначала COMSAFEARRAY, потом ADO) на чтение одного файла. После каждого чтения перезагрузка.

    Результат.

    Колонок: 6 Строк: 11 999

    Выполнение скрипта заняло: 5 323 мсек.

    Выполнение скрипта заняло: 2 880 мсек.

    Колонок: 6 Строк: 24 661

    Выполнение скрипта заняло: 7 061 мсек.

    Выполнение скрипта заняло: 5 551 мсек.

    Колонок: 6 Строк: 49 321

    Выполнение скрипта заняло: 7 890 мсек.

    Выполнение скрипта заняло: 10 335 мсек.

    Видно, что увеличение количества строк сильно сказывается при чтении через ADO

    2 тест. Чтение последовательно 3х файлов разными методами (сначала COMSAFEARRAY, потом ADO). После каждого чтения перезагрузка.

    Закешировал создаваемые процедурами Com объекты.

    Результат.

    Выполнение скрипта заняло: 3 067 мсек.

    Обработано строк: 6 колонок: 11 999 итого ячеек; 71 994

    Выполнение скрипта заняло: 1 182 мсек.

    Обработано строк: 6 колонок: 24 661 итого ячеек; 147 966

    Выполнение скрипта заняло: 2 274 мсек.

    Обработано строк: 6 колонок: 49 321 итого ячеек; 295 926

    Выполнение скрипта заняло: 2 450 мсек.

    Обработано строк: 6 колонок: 11 998 итого ячеек; 71 988

    Выполнение скрипта заняло: 4 917 мсек.

    Обработано строк: 6 колонок: 24 660 итого ячеек; 147 960

    Выполнение скрипта заняло: 9 849 мсек.

    Обработано строк: 6 колонок: 49 320 итого ячеек; 295 920

    Для метода чтения через COMSAFEARRAY замерил время создания Com объекта Екселя. Время создания COM Excel :2 001 мсек.

    3 тест. Собственно и не тест а замер производительности средствами 1С.

    Результат.

    При чтении через ADO больше всего времени занимает выполнение вот этой строки (70%):

    СтрокаТЗ[«Колонка»+Строка(Сч)] = НаборДанных.Fields.Item(Сч-1).Value;

    Обработка всего цикла считывания записей из recordset занимает 80% общего времени.

    При чтении через COMSAFEARRAY 27% времени уходит на открытие файла.

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

    10% на ТЗ.Добавить(); 🙂

    Reply ↓
  31. wildhog

    (25)

    Здесь

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

    «Данные» — тип ComSafeArray.

    «ТЗ» — Тип таблица значений (самая что нинаеть 1совская 🙂 )

    Чуть выше ж есть «ТЗ = Новый ТаблицаЗначений;»

    А вот Данные[Счетчик] — уже типа МАССИВ. Он то легко и загружается методом

    таблицы значений «ЗагрузитьКолонку».

    Reply ↓
  32. wildhog

    (22) Если нужно прочитать данные сохранив именно те типы которые определены в Экселе, то в сабже нужно сформировать колонки таблицы значений заранее, указав соответствующие типы. В этом случае значения в результирующей ТЗ будут приводиться к типу колонки ТЗ.

    Reply ↓
  33. Душелов

    Попробуйте затестить еще на 70000 строках 😉

    Reply ↓
  34. Душелов

    (29) >Согласен, что не все 1С-совское, но согласись, мы замеряем не скорость работы какого-то определенного интерфейса, а скорость операции импорта-экспорта

    >Вот из ComSafeArray/ надо из Тз — засунь ТЗ в массив и выведи

    Это надо учитывать в подсчете времени теста!

    Условия задачи надо определить жестче:

    1) Из экзеля загрузить в ТЗ данные.

    2) Из ТЗ записать данные в экзелевский файл.

    (31) Да я знаю, я говорю автору теста kadr, о том, что тесты не объективны, т.к. у него в тестах отсутствует заполнение ТЗ, которое так же занимает время.

    Reply ↓
  35. kadr

    (34) как же отсутствует?

    посмотри начальные тесты (7) там все есть…

    затем я просто выложил голое время без создания, инициализации и заполнения объектов

    Reply ↓
  36. wildhog

    (34) а как сделать 70000 строк? у мну получилось только 65536?

    Reply ↓
  37. Душелов

    (35) я про (23), в моем примере ты заполняешь строку ТЗ.

    Reply ↓
  38. Душелов

    (36) Сделай в 8-ке и сохрани в экзель 😉

    Reply ↓
  39. kadr

    (37) согласен

    исправим первый пример с учетом загрузки ТЗ оптом

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Область = ЛистЭксель.Range(ЛистЭксель.Cells(1,1), ЛистЭксель.Cells(ВсегоСтрок,ВсегоКолонок));

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

    Для Счетчик = 0 По ВсегоКолонок-1 Цикл

    ТЗ.ЗагрузитьКолонку(Данные[Счетчик], Счетчик);

    КонецЦикла;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Выполнение скрипта заняло: 141 мсек.

    Обработано строк: 8 колонок: 10000 итого ячеек; 80000

    И построчно

    ВремяНачала = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Область = ЛистЭксель.Range(ЛистЭксель.Cells(1,1), ЛистЭксель.Cells(ВсегоСтрок,ВсегоКолонок));

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

    Для Сч = 1 По ВсегоКолонок Цикл

    Для Сч1 = 1 По ВсегоСтрок Цикл

    ТЗ[Сч1-1][Сч-1] = Данные[Сч-1][Сч1-1];

    КонецЦикла;

    КонецЦикла;

    ВремяОкончания = Формат(Скрипт.eval(«new Date().getTime()»),»ЧГ=0″);

    Выполнение скрипта заняло: 828 мсек.

    Обработано строк: 8 колонок: 10000 итого ячеек; 80000

    Reply ↓
  40. wildhog

    (38) попробовал… алгоритм взял из (29).

    Что-то мне подсказывало что ничего не получиться 🙂 . Так и вышло…

    Reply ↓
  41. Душелов

    (40) Это один из главных минусол ОЛЕ екзеля, ниже 2007 версии 😉

    Reply ↓
  42. Душелов

    (39) 🙂 Сейчас я поколдую с массивами, ибо компонента должна была работать и в 7-ке, а она (до не давнего времени) с комсэйф-массивами не работала, и можно будет протестировать.

    Reply ↓
  43. kadr

    (40)

    Непроверенные данные

    http://excelsecrets.ru/post_1230987214.html

    Reply ↓
  44. wildhog

    (41) млин туплю. Работал с файлами xls а не xlsx… Офис у мну 2007.

    Все нормально создалось ну и загрузилось соответственно.

    Reply ↓
  45. Душелов

    (44) Результаты тестов какие?

    Reply ↓
  46. Душелов

    (39) Ради эксперимента, попробуй в рег.бат компоненты моей после строки

    regasm.exe ExcelEditor.dll /codebase

    добавить строку

    ngen.exe ExcelEditor.dll

    и перерегить.

    Reply ↓
  47. Душелов

    (44) xlsx уже не объективно 😉 формат другой. С ним можно протестить ради эксперимента http://infostart.ru/projects/3495/

    Reply ↓
  48. wildhog

    (47, 45) для xlsx

    Выполнение скрипта заняло: 13 240 мсек.

    Обработано строк: 6 колонок: 123 301 итого ячеек; 739 806

    Тут файл замера производительности http://slil.ru/27691047

    Reply ↓
  49. wildhog

    (47) Если честно. так и не вкурил как создать файл XLS с количеством строк более 65536. Выложи куда нить готовый файл протестирую.

    Reply ↓
  50. Душелов

    (48) А по ссылке в (47) время замера можешь сделать?

    (49) Сделай отчет в 1С-ке и сохранить табличный документ в экзель.

    Reply ↓
  51. wildhog

    (50) Попробую.

    Поделюсь еще мыслями. Вспомнил что у recorseta есть метод GetRows()

    Тут нашел полное описание http://support.microsoft.com/kb/246335

    Но, оказалось, что получаемый массив нужно еще транспонировать, что скорее всего очень долго (м.б попозже попробую реализовать). Имхо этот метод (getrows)

    не принесет ощутимого прироста в скорости, хотя, все возможно. 🙂

    Reply ↓
  52. Душелов

    (51) В моем компоненте аналогичное добавил. Обновление залил.

    Но, думаю, тоже не сильно прироста скорости будет… Хотя кто знает… Протестить надо. Метод «ПолучитьЗначения».

    Reply ↓
  53. wildhog

    Замер производительности по (47) — http://slil.ru/27691324

    Количество строк: 123 301

    Количество колонок: 6

    Reply ↓
  54. Душелов

    (53) А времени сколько вышло?

    А файл для какого приложения? У меня ничего не стоит с этим расширением ассоциированного…

    Reply ↓
  55. wildhog

    (54) Время в замере. Отдельно замерял. Но счет шел на минуты.

    Файл xlsx.

    Тестировал обработку чтения файла екселя средствами 1С которая из (47)

    http://infostart.ru/projects/3495/

    Функция взял из readme.

    Reply ↓
  56. Душелов

    (55) Ну если на минуты, то тогда не надо 🙂

    Reply ↓
  57. wildhog

    щас пробую ВК…

    Reply ↓
  58. kadr

    (46) чтение чуть быстрее стало

    Выполнение скрипта заняло: 1735 мсек.

    Reply ↓
  59. wildhog

    (56) Итак. Тест твоей компоненты.

    Выполнение скрипта заняло: 6 621 мсек.

    Обработано колонок : 7

    Обработано строк : 64 299

    Тот же файл, только по сабжу:

    Выполнение скрипта заняло: 7 989 мсек.

    Обработано строк: 7 колонок: 64 299 итого ячеек; 450 093

    Опять же повторное чтение с помощью сабжа (сразу после первого):

    Выполнение скрипта заняло: 3 997 мсек.

    Обработано строк: 7 колонок: 64 299 итого ячеек; 450 093

    Скажи. а в ВК используешь GetRows?

    Reply ↓
  60. wildhog

    (59) + файл замера производительности. http://slil.ru/27691513

    Reply ↓
  61. Душелов

    (59) Ну, примерно, аналогичное GetRows, но массив я все равно «ручками» заполняю.

    И это ты тестил с «ПолучитьЗначения» ?

    Reply ↓
  62. wildhog

    (61) Угу. пробежался по колонкам и загрузил данные

    Экзель.ОткрытьФайл(ПутьКФайлу);

    Экзель.ОткрытьЛист(1);

    ВсегоКолонок = Экзель.ПолучитьКоличествоКолонок();

    ВсегоСтрок = Экзель.ПолучитьКоличествоСтрок();

    Для Счетчик = 1 По ВсегоСтрок Цикл

    ТЗ.Добавить();

    КонецЦикла;

    Для Счетчик = 0 По ВсегоКолонок — 1 Цикл

    Массив = Экзель.ПолучитьЗначения(Счетчик).Выгрузить();

    ТЗ.ЗагрузитьКолонку(Массив, Счетчик);

    КонецЦикла;

    Reply ↓
  63. PeRom

    Пользую метод давно, хотел поделится, но никак не могу справиться с проблеммой, можт кто сталкивался?

    Код: Файл = Новый COMОбъект(«Excel.Application»);

    Файл.DisplayAlerts = 0; //не задавать вопросы

    Файл.Application.AutomationSecurity = 3; //уровень безопасности

    Книга = Файл.Workbooks.Open(ФайлХЛ);

    Лист = Книга.Worksheets(2);

    Пока (НЕ ПустаяСтрока(Строка(Лист.Cells(строкаXL,1).Value))) или

    (НЕ ПустаяСтрока(Строка(Лист.Cells(строкаXL,2).Value))) Цикл

    строкаXL = строкаXL+1;

    КонецЦикла;

    COMSafeArrayМассив = Лист.range(Лист.Cells(1,1),Лист.Cells(строкаXL,249)).Value;

    Книга.Close();

    Массив = COMSafeArrayМассив.Выгрузить();…………

    Файл около 50 Мбайт с макросами и паролями(но с этим вроде разобрался), из одного листа файла загружаю 250 колонок * 6-10 000 строк, на строке Книга = Файл.Workbooks.Open(ФайлХЛ); висит 5-6 минут, остальное пролетает за секунды. Как можно это победить?

    Reply ↓
  64. wildhog

    (63) А сколько времени открывается файл в WIndows?

    попробуй отключать еще автопересчет формул, кажется свойство Calculation.

    М.б еще какие макросы запускаются при открытии?

    Если не поможет, м.б попробовать ADO? В этой ветке ado подробно осветили 🙂

    Reply ↓
  65. rasswet

    сильно вы! респект! так на чем в итоге то останавливаемся?

    Reply ↓
  66. Душелов

    (65) Я пользуюсь своей компонентой. И быстро, и без офиса. Т.к. у нас лицензионный софт везде, а офис не у всех стоит.

    Reply ↓
  67. rasswet

    (66) млин((

    написал чтение черед АДО. на работе работает. на домашнем компе тоже SP2 и

    MDAC версии 2000.85.1022- не работает.

    у того кому делал на висте -тоже не работает. у кого-то читает на висте через ADO

    через компоненту твою я не умею..хотелось штатными (относительно) средствами.

    Reply ↓
  68. Душелов

    (67) В чем проблема с компонентой?

    АДО не работает как? Что пишет? Может поставщика данных нет в системе?

    Reply ↓
  69. rasswet

    (68) компоненту не хочу, это слать клиенту длл, объяснять по телефону чё с ней делать..не. не хочу.

    адо пишет «»BOF или EOF имеют значение True или текущая запись была удалена»»

    Reply ↓
  70. rasswet

    (68) проблема локализовалась. в тестовых файлах было по 1му листу, и грузилось нормально, а в реале оказалось три листа в фале. пофиксил.

    Reply ↓
  71. CheBurator

    юзайте йоксель…

    а адо как-то попробовал -и бросил ибо на первом же подсунутом файле читала половину файла и тупо финишировала как будто вконец данных — так победить и не смог…

    Reply ↓
  72. Slim33rus

    Спасибо wilghog! 😀 Очень помогла статья. Эксель из 12600 строк загружается за 10 сек. Раньше загружаллось за 8 мин. Большое спасибо. 😀

    Reply ↓
  73. sashs1980

    Этот способ несомненно быстрее, но не совсем корректен и следовательно не универсален. Дело в том, что при выполнение Данные = Область.Value.Выгрузить() текст преобразуется, например у меня был артикул 0000056789 и он преобразуется в 56 789. А при стандартном (универсальном)по ячеестым обходом нет, когда вытаскиваешь из ячейки .text

    Reply ↓
  74. wildhog
    sashs1980 пишет:

    Этот способ несомненно быстрее, но не совсем корректен и следовательно не универсален. Дело в том, что при выполнение Данные = Область.Value.Выгрузить() текст преобразуется, например у меня был артикул 0000056789 и он преобразуется в 56 789. А при стандартном (универсальном)по ячеестым обходом нет, когда вытаскиваешь из ячейки .text

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

    Reply ↓
  75. Stepan_1c

    Спасибо, коротко и ясно=)

    Reply ↓
  76. capshow

    Спасибо за наВодку с методами Excel ) Всегда уважительно относился к емким и эффективным алгоритмам! Респект!

    Reply ↓
  77. mikhailovaew

    Эх, вот такую бы штуку для 7.7! Пробовала переписать — спотыкается о строку

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

    говорит — «Ожидается скалярное выражение (Данные)».

    Кто-нибудь знает, как научить семерку жрать массивы?

    Reply ↓
  78. redgoll

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

    Reply ↓
  79. wildhog

    redgoll

    Проблема в том, что используешь либо запрос либо НайтиПо… в цикле. Что очень медленно…

    Помещай ТЗ во временную таблицу и ищи элементы запросом через левое/внутреннее соединение с ней.

    Не забудь, что для временнной таблицы важно чтобы колонки ТЗ были типизированы.

    Reply ↓
  80. redgoll

    Спасибо, за совет. Попробую воспользоваться вашим методом. Потом отпишусь о результатах и поблагодарю отдельно. Когда-то читал на данном ресурсе про метод загрузки данных не из Excel, а из табличного документа (mxl). Автор помнится очень хвалил его за быстродействие. Не пробовали на такие грабли наступать?

    Reply ↓
  81. wildhog

    (80)

    Не пробовал. Но, думаю, что если и будет прирост в скорости, то не очень большой. + преобразование в mxl занимает время. Так что, смысла не вижу… Попробуйте, вдруг сабжевый способ не самый быстрый )

    Reply ↓
  82. wildhog

    (82) Если дадите свою компоненту — погоняю, проверим.

    А что на выходе 1Совская ТЗ или что-то еще?

    У любого способа есть плюсы и минусы. У моего — универсальность, независимость от ВК (права пользователей, регистрация и т.д). У вашего скорость.

    Reply ↓
  83. wildhog

    (85) Eugeneer, не стал писать про деньги )) Для многих это существенный минус…

    Надеюсь продажи Вашей обработки говорят об обратном ))

    Reply ↓
  84. uri1978

    Отличный метод. Автору большой плюс!

    Reply ↓
  85. kiruha

    (82)

    Здорово конечно, но приведенный автором статьи алгоритм + дополнение по записи от kadr полностью покрывает все проблемы — 0.4 сек на 80 000 ячеек.

    В дальнейшем Вам стоит указывать и на это альтернативное решение

    Reply ↓
  86. ram3

    (74)

    приведение типов происходит еще при выгрузке в массив Данные = Область.Value.Выгрузить(), к моменту заполнения таблицы значений артикул 0000056789 уже принимает следующий вид 56 789.

    Какие еще есть идеи? Способ очень хорош, но вышеописанный момент все портит…

    Reply ↓
  87. wildhog

    (91) добрый день. Пришлите файл, посмотрю. + попробуйте перед чтением типизировать колонки тз.

    Reply ↓
  88. ram3

    (92)

    в том-то и дело, что до ТЗ не доходит, смотрю отладчиком массив Данные сразу после выгрузки в него области — там уже значения изменены…

    Файл для примера: http://yadi.sk/d/21WzBuG5JSKpR

    Reply ↓
  89. kiruha

    (92)

    Смотрю в вашем файле формат ячеек — то числовой (дополнительный), то числовой (все форматы), то общий (текстовой и числовой форматы, еще форматы)

    Это ж ….

    В принципе можно было бы отформатировать Область.NumberFormat=»Text»;

    Но у Вас в ячейке визуально 00000141 — фактически в ячейке число 141, числовой формат дополнительный, греческий язык , тип «табельный номер» — который отображает 141 как 00000141 и далее аналогично

    Reply ↓
  90. wildhog

    (93) По идее Область.Value берет именно ЗНАЧЕНИЯ ячеек, а не форматированные значения. В (94) все верно kiruha написал — проблемы в данных файла.

    Поэтому, приводите в «нормальный» формат файл, а потом читайте.

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

    Reply ↓
  91. ram3

    (94) kiruha,

    Но при всем при этом, ЛистЭксель.Cells(СчетчикСтрок,СчетчикКолонок).Text возвращает именно 00000141 а не 141, как вы предполагаете!

    Reply ↓
  92. ram3

    (95)

    так и сделал, читаю по старинке перебором ячеек. Жаль, но в данной реализации ваш метод далеко не универсален (предварительная обработка файла убивает все плюсы), хотя по скорости видимо лучший! Пойду на форум любителей экселя, может там что-то подскажут… В любом случае спасибо, идея отличная!

    Reply ↓
  93. wildhog

    (97) Ну уж вы хотите и скорость и чтобы всякое повидло из файлов вытягивало. Удачи)

    Reply ↓
  94. ram3

    (98)

    к сожалению боевые условия не всегда приближены к идеальным: как правило файлики для загрузки мы не сами себе стряпаем.

    Reply ↓
  95. Serg O.

    Еще есть способ — быстрой «вставки» таблицы из Excel в 1С — через копирование -вставку… (копипаст)

    программным способом еще года 2 назад сам делал… через Wscript.Shell

    «доработка» стандартной обработки загрузки из табличного документа

    функция мПрочитатьТабличныйДокументИзExcel()

    // Функция считывает в табличный документ данные из файла в формате Excel

    //

    // Параметры:

    // ТабличныйДокумент — ТабличныйДокумент, в который необходимо прочитать данные

    // ИмяФайла — имя файла в формате Excel, из которого необходимо прочитать данные

    // НомерЛистаExcel — номер листа книги Excel, из которого необходимо прочитать данные

    //

    // Возвращаемое значение:

    // Истина, если файл прочитан, Ложь — иначе

    //

    Функция мПрочитатьТабличныйДокументИзExcel(ТабличныйДокумент, ИмяФайла, НомерЛистаExcel = 1) Экспорт

    xlLastCell = 11;

    ВыбФайл = Новый Файл(ИмяФайла);

    Если НЕ ВыбФайл.Существует() Тогда

    Сообщить(«Файл не существует!»);

    Возврат Ложь;

    КонецЕсли;

    Попытка

    Excel = Новый COMОбъект(«Excel.Application»);

    Excel.WorkBooks.Open(ИмяФайла);

    Состояние(«Обработка файла Microsoft Excel…»);

    ExcelЛист = Excel.Sheets(НомерЛистаExcel);

    Исключение

    Сообщить(«Ошибка. Возможно неверно указан номер листа книги Excel.»);

    Возврат ложь;

    КонецПопытки;

    ТабличныйДокумент.Очистить();

    ActiveCell = Excel.ActiveCell.SpecialCells(xlLastCell);

    RowCount = ActiveCell.Row;

    ColumnCount = ActiveCell.Column;

    //+++ через буфер обмена

    попытка

    Excel.ActiveCell.SpecialCells(xlLastCell).Select();

    Excel.Range( Excel.Selection, Excel.Cells(1)).Select();

    Wsh = новый COMобъект(«Wscript.Shell»);

    Excel.Selection.Copy();

    Wsh.sendkeys(«#k8SjZc9Dxkc»);

    Предупреждение(«Нажмите кнопку [Да] в следующем окна.

    |Требуется сохранить данные в «»Буфер обмена»»

    |для быстрой вставки сразу всех данных в 1С…»,30);

    Excel.WorkBooks.Close();

    Excel = 0;

    //// Возврат Объект.ParentWindow.ClipboardData.Getdata(«Text»);

    //// Снимим защиту и разрешим вывод

    ТабличныйДокумент.Защита = Ложь;

    ТабличныйДокумент.Вывод = ИспользованиеВывода.Разрешить;

    ТабличныйДокумент.ТекущаяОбласть = ТабличныйДокумент.Область(1, 1, RowCount, ColumnCount);

    рез = истина;

    исключение

    Предупреждение(ОписаниеОшибки());

    рез = ложь;

    КонецПопытки;

    Предупреждение(«Нажмите [Ок] для «»мгновенной»» вставки «+строка(RowCount)+» строк из файла…»);

    Wsh.sendkeys(«#k8SjZc9Dxkv»);

    возврат рез;

    //+++)

    КонецФункции

    Reply ↓
  96. TVA_11

    (1) wildhog, благодарю!

    Очень помогло.

    Reply ↓
  97. Alien_job

    вот эти 3 строчки искал в другом месте. Было бы здорово их включить в статью

    Эксель = Новый COMОбъект(«Excel.Application»);
    Эксель.WorkBooks.Open(ИмяФайла);
    ЛистЭксель = Эксель.WorkSheets(1);
    
    Reply ↓
  98. wildhog

    (118) В статье основной посыл это метод работы с данными через comsafearray. А каким образом получен объект WorkSheets — вторично. В моем примере вообще читается текущий лист последней активной книги Excel.

    Поэтому, спасибо за предложение, но в данному случае, имхо, это лишнее. К тому же в (7) этот код уже есть 😉

    Reply ↓
  99. adapter

    можно еще короче — открыть файл, удалить пустые строкистолбы вначале, использовать код

    Excel = ПолучитьCOMОбъект(Объект.ПутьКФайлу);
    Данные = Excel.ActiveSheet.UsedRange().Value.Выгрузить();
    

    Особенности:

    Читает сразу из открытого файла

    Автоматический поиск заполненых ячеек. Если в начале есть пустые строкистолбы, то Excel может не правильно определить используемый диапазон (много лишнего). Проверить можно на листе нажав ctrl+shift+space

    Обойти можно выделив вначале первую ячейку данных

    Excel = ПолучитьCOMОбъект(Объект.ПутьКФайлу,»Excel.Application»);
    Excel.Worksheets(1).Range(«B1»).Select();
    Данные = Excel.ActiveSheet.UsedRange().Value.Выгрузить();
    

    ну и в последних релизах ПолучитьCOMОбъект перестал работать из не открытого файла. Если нужно из закрытого то

    Excel = Новый COMОбъект(«Excel.Application»);
    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *