13.06.2006, 19:19 | #11 |
Moderator
|
Ну, я сам немножко поковырялся... не с целью бития рекордов, а как раз наоборот – с целью небольшой разборки, направленной на выяснения некоторого предела низкой скорости - типа "медленнее которой остается только способ вписывания значений в ячейки Excel шариковой ручкой"
Абстрагируемся на некоторое время от точности получаемых результатов (я имею в виду, в частности, потенциальные проблемы с пресловутыми ведущими нулями в текстовых значениях, состоящих из цифр, а также потерю точности в длинных цифровых текстовых кодах, например, в 20-тизначных банковских счетах, где возможно обнуление последних 5 разрядов) и сосредоточимся исключительно на скорости вывода (пусть пока текст "000333" пишется в ячейку Excel как число 333, а дата "13.06.06" как число 38881 - просто тупо посмотрим на длительность всего процесса в целом). Способы, представленные AndyD и Recoilme, демонстрируют высокую скорость выгрузки. Задался вопросом: а как поведет себя на тестовом задании популярный класс ComExcelDocument_RU? Примерно представлял, что "фигово" (о низкой скорости уже "слышал" на Форуме). Но захотелось оценить меру "фиговости", поэтому обо всём по порядку. Этот класс в своем первозданном виде (слой DIS) предоставляет нам в распоряжение 2 своих метода, которые можно использовать для решения нашей тестовой задачи: 1. метод static str numToNameCell(int _iCol, int _iRow) - например, для получения текстового адреса ячейки "B1" по номеру строки = 1 и по номеру столбца = 2 2. метод public void insertValue(BookMark _bookMark, anyType _anyVal, int _workSheet = 1) - для записи значения в эту ячейку "B1". Набросал тестовый джоб следующего вида: X++: static void SpeedTest_Job1(Args _args) { LedgerTrans ledgerTrans; LedgerTable ledgerTable; ComExcelDocument_RU excel; int row; int timeFullStart, timeFullFinish, timeFullTotal; ; timeFullStart = timenow(); excel = new ComExcelDocument_RU(); excel.newFile("", true, -1); row = 0; while select ledgerTrans join ledgerTable where ledgerTrans.AccountNum == ledgerTable.AccountNum { row++; if (row > 50000) break; excel.insertValue(ComExcelDocument_RU::numToNameCell( 1, row), ledgerTrans.RecId); excel.insertValue(ComExcelDocument_RU::numToNameCell( 2, row), ledgerTrans.AccountNum); excel.insertValue(ComExcelDocument_RU::numToNameCell( 3, row), ledgerTable.AccountName); excel.insertValue(ComExcelDocument_RU::numToNameCell( 4, row), strfmt('%1', ledgerTable.AccountPlType)); excel.insertValue(ComExcelDocument_RU::numToNameCell( 5, row), ledgerTrans.BondBatchTrans_RU); excel.insertValue(ComExcelDocument_RU::numToNameCell( 6, row), ledgerTrans.BondBatch_RU); excel.insertValue(ComExcelDocument_RU::numToNameCell( 7, row), ledgerTrans.TransDate); excel.insertValue(ComExcelDocument_RU::numToNameCell( 8, row), ledgerTrans.Txt); excel.insertValue(ComExcelDocument_RU::numToNameCell( 9, row), ledgerTrans.AmountMST); excel.insertValue(ComExcelDocument_RU::numToNameCell(10, row), strfmt('%1', ledgerTrans.Crediting)); } timeFullFinish = timenow(); timeFullTotal = timeFullFinish - timeFullStart; info('Время выполнения, сек'); info(int2str(timeFullTotal)); } Время выполнения джоба - 3615 сек, т.е. практически ровно 1 час. Для сравнения: время выполнения на этом же компе джобов AndyD и Recoilme - 29 сек и 12 сек соответственно. Решил заглянуть внутрь этих двух методов класса ComExcelDocument_RU. Заглянул. Расстроился… Есть ощущение, что адресация к ячейкам воплощалась по принципу "Что вижу в Excel - о том пою. Как вручную работаю - так и запрограммирую. Вижу ячейки в первой строке Excel - A1, B1, C1... но, однако, смекаю, что вывод на лист, скорее всего, будет производиться в двух циклах: внешний - по строкам, тут всё в порядке: 1, 2, 3, 4, 5... и внутренний - по столбцам - здесь неудобство: порядок чисел-номеров колонок 1, 2, 3, 4, 5... нужно превращать в буквы А, B, C, D, E... С одной целью - чтобы потом сцепить букву столбца и цифру строки и выйти на какой-нибудь Range("A1"), и дальше через метод insertValue задать значение свойства Value2". Возникает вопрос: А ЗА-ЧЕМ так, мягко говоря, сложно? Со времен версии Excel 5 (1994 год) - первой версии Excel, когда вместо специфического макроязыка версии 4 пришёл Visual Basic - так вот, со времен версии 5 в Excel присутствует способ адресации к ячейкам листа при помощи метода Cells(номер строки, номер столбца), предназначенный как раз для нужд циклического перебора ячеек. Для обращения к прямоугольным диапазонам ячеек используется формат Range(Cells(1-я строка диапазона, 1-й столбец диап-на), Cells(посл. Строка диап-на, посл.столбец диап-на)) - а не только адресация вида Range("A1:H150"), как это пишет макрорекордер на VBA в Excel. Существуют другие полезные методы адресации, например, Offset... Набросал второй тестовый джоб: X++: static void SpeedTest_Job2(Args _args) { LedgerTrans ledgerTrans; LedgerTable ledgerTable; ComExcelDocument_RU excel; COM doc; COM actSheet; COM cells; int row; int timeFullStart, timeFullFinish, timeFullTotal; ; timeFullStart = timenow(); excel = new ComExcelDocument_RU(); excel.newFile("", true, -1); doc = excel.getComDocument(); actSheet = doc.ActiveSheet(); cells = actSheet.Cells(); row = 0; while select ledgerTrans join ledgerTable where ledgerTrans.AccountNum == ledgerTable.AccountNum { row++; if (row > 50000) break; COM::createFromVariant( cells.Item(row, 1) ).Value2( ledgerTrans.RecId ); COM::createFromVariant( cells.Item(row, 2) ).Value2( ledgerTrans.AccountNum ); COM::createFromVariant( cells.Item(row, 3) ).Value2( ledgerTable.AccountName ); COM::createFromVariant( cells.Item(row, 4) ).Value2( strfmt('%1', ledgerTable.AccountPlType) ); COM::createFromVariant( cells.Item(row, 5) ).Value2( ledgerTrans.BondBatchTrans_RU ); COM::createFromVariant( cells.Item(row, 6) ).Value2( ledgerTrans.BondBatch_RU ); COM::createFromVariant( cells.Item(row, 7) ).Value2( ledgerTrans.TransDate ); COM::createFromVariant( cells.Item(row, 8) ).Value2( ledgerTrans.Txt ); COM::createFromVariant( cells.Item(row, 9) ).Value2( ledgerTrans.AmountMST ); COM::createFromVariant( cells.Item(row, 10) ).Value2( strfmt('%1', ledgerTrans.Crediting) ); } timeFullFinish = timenow(); timeFullTotal = timeFullFinish - timeFullStart; info('Время выполнения, сек'); info(int2str(timeFullTotal)); } Опытные коллеги наверняка знают обо всем об этом. Обидно за новичков, которые приходят в Аксу, сталкиваются с ComExcelDocument_RU как с неким "стандартом де-факто" и первое время вынуждены идти по этому, как мне кажется, не совсем верному пути... Теряя это самое драгоценное время! |
|
|
За это сообщение автора поблагодарили: mazzy (5), DreamCreator (3), JeS (1), Silphidae (1). |
Теги |
benchmark, download, excel, faq, xml, законченный пример, производительность, экспорт/импорт |
|
|