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




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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 не будет опубликован. Обязательные поля помечены *