AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: База знаний и проекты
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 08.06.2006, 16:38   #21  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Эххх, вот если б вы еще и готовые классы выкладывали ;(
Или хотя бы первые три места по скорости этого забега
Старый 08.06.2006, 19:13   #22  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Recoilme
Попробуйте так:
PHP код:
strfmt('%1%2',"'"it.ItemId
Да, вспоминаю, что такой способ ввода текста в ячейку Excel с ведущим апострофом действительно есть...
Рассмотрим, однако, что происходит при таком подходе.

Допустим у нас есть код номенклатуры ItemId = "000333" (строка - 6 символов). При выгрузке в файл запишется 7 символов: '000333

Для упрощения анализа создадим вручную текстовый файл с единственной строкой: '000333 (7 символов), cохраним его под именем test000333.xls и откроем его Excel-ем:

1. В открывшемся Excel в ячейке А1 так и отображается '000333 т.е. с ведущим апострофом.
2. Введем в соседнюю ячейку B1 формулу: =ДЛСТР(А1) . Результат = 7 (символов)
3. Введем далее в ячейку C1 формулу: =А1 & A1 . Результат = '000333'000333

4. Еще раз посмотрим, что мы имеем к этому времени:
- в ячейке А1 отображается: '000333
- в ячейке B1 отображается: 7
- в ячейке C1 отображается: '000333'000333

5. Войдём в режим редактирования ячейки А1, нажав на ней клавишу F2. И сразу выйдем, нажав Enter.

6. Посмотрим что получилось теперь:
- в ячейке А1 отображается: 000333
- в ячейке B1 отображается: 6
- в ячейке C1 отображается: 000333000333

(т.е. мы наконец достигли в ячейке А1 такой ситуации, которая складывается, когда пользователь вводит последовательность символов '000333 в самом Excel, а не грузит ее из текстового файла с расширением xls)

Выгрузка из Аксапты у Job70, конечно, очень быстрая...
Но вы думаете, пользователь будет счастлив от такого, пусть даже очень быстро полученного, результата?
Старый 09.06.2006, 09:31   #23  
Recoilme is offline
Recoilme
злыдень
Аватар для Recoilme
Злыдни
 
895 / 192 (8) ++++++
Регистрация: 18.06.2003
Насчет счастья не уверен, но мы работаем в таком формате несколько лет с поставщиками (B2B), в частности референсы товаров выгружаются с ведущим апострофом. Это, насколько мне известно, штатный способ задания типа текстового поля в эксель. Что Вы и продемонстрировали нажав F2 - эксель удалил ведущий апостроф. Если Вам известны другие способы принудительного форматирования ячеек - было бы интересно узнать о них.
__________________
Ибо зло есть лучшая сила человека. "Человек должен становиться все лучше и злее" -- так учу я. /Ф. Ницше/
Старый 09.06.2006, 10:04   #24  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,622 / 2925 (107) +++++++++
Регистрация: 16.01.2004
Записей в блоге: 5
Цитата:
Сообщение от Gustav
НО! Текстовое поле ItemId становится числовым с откусыванием ведущих нулей ("00333" -> 333). Можно исправить ситуацию, например, каким-нибудь тегом?
1. поменяйте формат ячейки на текстовый
2. выгрузите в XML и сравните

Будет что-то типа:
Код:
<Style ss:ID="s21">
 <NumberFormat ss:Format="@"/>
</Style>
...
<Cell ss:StyleID="s21"><Data ss:Type="String">00001</Data></Cell>
PS. Еще раз: надо сделать искейпинг спецсимволов XML чтобы

test&data выгрудалось как test&amp;data а то XL может не понять
Старый 09.06.2006, 10:20   #25  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Recoilme
Что Вы и продемонстрировали нажав F2 - эксель удалил ведущий апостроф.
Если Вам известны другие способы принудительного форматирования ячеек - было бы интересно узнать о них.
Ну, Excel его не совсем удалил. Апостроф не отображается в таблице. Но если мы войдём в режим редактирования этой ячейки, то увидим что "внутри" ячейки символ по-прежнему присутствует, теперь выполняя свою именно форматирующую функцию. "Ну это нормально!" (с) В.Мошков в фильме "Охота на пиранью"

"Другие способы принудительного форматирования" - это контролируемо выводить данные в родную таблицу Excel, а не в текстовый файл, который потом неконтролируемо открывается. В xml-способе AndyD-а в этой ветке ведущие нули сохраняются. Сохраняются они и в моем 2-м джобе в теме Поговорим об ADO . Здесь есть, правда, другая крайность: если пользователь зайдет в такую ячейку и просто нажмет Enter, то ведущие нули пропадут и строка "000333" превратится в число 333. Но это можно победить, если по окончании собственно вывода форматировать кодом такие проблеменые колонки как текст (т.е. всё по-прежнему под контролем). К небольшому сожалению, xml-способ AndyD-а работает только начиная с версии Excel 2002 (XP), но, с другой стороны, мы же должны смотреть вперед, а не только постоянно оглядываться назад...
Старый 09.06.2006, 10:26   #26  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от belugin
1. поменяйте формат ячейки на текстовый
Максим, я сам - всецело ЗА! Но я говорю сейчас о способе выгрузки, предложенном Recoilme. Т.е. немножко критикую этот момент, связанный с цифровым текстом с ведущими нулями.
Старый 09.06.2006, 11:00   #27  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
2 belugin
Насчет & и т.п.
Excel нормально отрабатывает их присутствие в данных, единственно - если угловые скобки идут парой <> - то будет ошибка формата.
Но вообще согласен - надо отрабатывать такие ситуации.

Вот код функции, заменяющей спецсимволы в строке
X++:
    str 1 entity[5];
    str 6 entityReference[5];
    str getEntities(str s1)
    {
        int j, pos;
        ;
        for (j=1;j<=5;j++)
        {
            pos = strscan(s1, entity[j], 1, strlen(s1));
            while (pos)
            {
                s1 = substr(s1, 1, pos-1) + entityReference[j] + substr(s1, pos+1, strlen(s1));
                pos = strscan(s1, entity[j], pos + 1, strlen(s1));
            }
        }
        return s1;
    }
    ;
    entity[1] = "&";
    entity[2] = "<";
    entity[3] = ">";
    entity[4] = "'";
    entity[5] = '"';
    entityReference[1] = "&amp;";
    entityReference[2] = "&lt;";
    entityReference[3] = "&gt;";
    entityReference[4] = "&apos;";
    entityReference[5] = "&quot;";
....
        buf +=
            strfmt(
...
                ledgerTrans.RecId, getEntities(ledgerTrans.AccountNum), getEntities(ledgerTable.AccountName), ledgerTable.AccountPlType,
                ledgerTrans.BondBatchTrans_RU, ledgerTrans.BondBatch_RU,
                date2str(ledgerTrans.TransDate, 321, 2, 3, 2, 3, 4), getEntities(ledgerTrans.Txt),
                strltrim(strrem(num2str(ledgerTrans.AmountMST, 10, 2, 1, 0), "+")), ledgerTrans.Crediting,
                #toFile ? "" : (cnt mod 2 == 1 ? " ss:StyleID=\"s15\"" : " ss:StyleID=\"s16\""),
                cnt mod 2 == 1 ? " ss:StyleID=\"s12\"" : " ss:StyleID=\"s13\"",
                cnt mod 2 == 1 ? " ss:StyleID=\"s10\"" : " ss:StyleID=\"s11\"",
                #toFile ? (cnt mod 2 == 1 ? " ss:StyleID=\"s15\"" : " ss:StyleID=\"s16\"") : "");
...
Время загрузки увеличивается на 3-4 с

PS упс. исправил
__________________
Axapta v.3.0 sp5 kr2

Последний раз редактировалось AndyD; 09.06.2006 в 11:43.
Старый 13.06.2006, 12:12   #28  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Коллеги, на самом деле, наш фестиваль-"мундиаль" различных способов экспорта Axapta => Excel продолжается. Приглашаю вас к активному участию.

Хотелось бы увидеть данные по CSV, DDE, Clipboard+PasteSpecial и др.

Экспортеры всех видов, присоединяйтесь!

P.S. (пока идёт чемпионат мира по футболу, слово "мундиаль" считается приличным )
Старый 13.06.2006, 19:19   #29  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Ну, я сам немножко поковырялся... не с целью бития рекордов, а как раз наоборот – с целью небольшой разборки, направленной на выяснения некоторого предела низкой скорости - типа "медленнее которой остается только способ вписывания значений в ячейки 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));
 
}
Джоб был выполнен на достаточно мощном сервере с процессором AMD, 2.5 GHz, 4 Gb. БД - на другой машине, Oracle. Сетка - 100 Mb. Excel - 2003. Axapta - 3.0 CIS SP3, без AOS.

Время выполнения джоба - 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));
 
}
Время выполнения - 1090 сек. Т.е. 18 минут. Или более, чем в 3 раза быстрее, чем первый джоб с "классической" адресацией ComExcelDocument_RU. Конечно, до "29 сек и 12 сек" - "как до Пекина...", но уже гораздо более оптимистично, чем "1 час". Троекратное увеличение скорости только за счет изменения способа адресации.

Опытные коллеги наверняка знают обо всем об этом. Обидно за новичков, которые приходят в Аксу, сталкиваются с ComExcelDocument_RU как с неким "стандартом де-факто" и первое время вынуждены идти по этому, как мне кажется, не совсем верному пути... Теряя это самое драгоценное время!
За это сообщение автора поблагодарили: mazzy (5), DreamCreator (3), JeS (1), Silphidae (1).
Старый 13.06.2006, 22:59   #30  
online
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,303 / 3533 (124) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
2Gustav: Читаю - прям как энциклопедию .... классно. Здорово получилось. (Пока еще раз одобрить не могу) Если выкрою время (в чем сомневаюсь, но можно постараться) - то надо будет еще 2 механизма проверить - через OWC и буфер обмена.
__________________
Возможно сделать все. Вопрос времени
Старый 14.06.2006, 10:45   #31  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
Еще один вариант - при помощи DDE.
Время выгрузки - 20-21 с

PS Так как пользователь может в настройках отключить возможность обработки запросов DDE, то, перед созданием класса clientDDE, можно вставить сброс этого флага
X++:
    app.IgnoreRemoteRequests(false);
    ddeClient = new DDEClient("Excel", sheet.Name());
PPS По просьбе Gustav перемещаю код в окно.
Заодно добавил выгрузку ч/з буфер обмена. Для выгрузки использую TextBuffer.toClipboart().
Для переключения используется макрос DDE. Значение TRUE - выгрузка ч/з DDE, значение FALSE - clipboard
Время выгрузки примерно одинаковое для обоих вариантов

X++:
static void ExportExcelDDE(Args _args)
{
    LedgerTrans ledgerTrans;
    LedgerTable ledgerTable;
    int timeFullStart, timeFullFinish, timeFullTotal;
    int cnt = 0;
    str Buf, s;
    ComExcelDocument_Ru excel;
    COM                 doc;
    COM                 app;
    COM                 sheet;
    COM                 range;
    DDEClient           ddeClient;
    TextBuffer          tb;

    #define.RowsCount(5000)
    #define.DDE(false)
    #define.xlR1C1(0xFFFFEFCA)

    #if.DDE(false)
        void LoadKeyboardLayout()
        {
            dll dll = new dll("user32");
            dllFunction dllFunction = new dllFunction(dll, "LoadKeyboardLayoutA");
            #define.KLF_ACTIVATE(1)
            ;
            dllFunction.arg(ExtTypes::String, ExtTypes::DWord);
            dllFunction.returns(ExtTypes::DWord);
            dllFunction.call("00000419", #KLF_ACTIVATE);
        }
    #endif
    ;
    // засекаем время
    timeFullStart = timenow();

    excel = new ComExcelDocument_Ru();
    excel.newFile("", false);
    doc = excel.getComDocument();
    app = doc.application();
    sheet = app.ActiveSheet();

    buf = "RecId\tAccountNum\tAccountName\tAccountPlType\tBondBatchTrans_RU\tBondBatch_RU\tTransDate\tTxt\tAmountMST\tCrediting\n";

    range = sheet.range("A:A;E:E");
    range.NumberFormat("0");
    range = sheet.range("B:D;F:F;H:H;J:J");
    range.NumberFormat("@");
    range = sheet.range("G:G");
    range.NumberFormat("ДД.ММ.ГГГГ");
    range = sheet.range("I:I");
    range.NumberFormat("# ##0,00");

    #IF.DDE(true)
        app.IgnoreRemoteRequests(false);
        ddeClient = new DDEClient("Excel", sheet.Name());
    #ENDIF
    #IF.DDE(false)
        tb = new textBuffer();
        LoadKeyboardLayout();
    #ENDIF

    cnt++;
    while select  ledgerTrans
            join  ledgerTable
            where ledgerTrans.AccountNum == ledgerTable.AccountNum
               && ledgerTrans.TransDate >= str2date('dd.mm.yy',123) && ledgerTrans.TransDate <= str2date('DD.MM.YY',123)
    {
            // 10 полей
        buf +=
            strfmt(
                "%1\t%2\t%3\t%4\t%5\t%6\t%7\t%8\t%9\t%10\n",
                ledgerTrans.RecId, ledgerTrans.AccountNum, ledgerTable.AccountName, ledgerTable.AccountPlType,
                ledgerTrans.BondBatchTrans_RU, ledgerTrans.BondBatch_RU,
                date2str(ledgerTrans.TransDate, 321, 2, 3, 2, 3, 4), ledgerTrans.Txt,
                strltrim(strrem(num2str(ledgerTrans.AmountMST, 10, 2, 2, 0), "+")), ledgerTrans.Crediting);

        cnt++;
        if (cnt > 0 && (cnt mod #RowsCount == 0))
        {
            s = strfmt("A%1:J%2",  cnt-#RowsCount+1, cnt);
            range = sheet.range(s);
            #IF.DDE(true)
                ddeClient.poke(range.Address(true, true, #xlR1C1), buf);
            #ENDIF
            #IF.DDE(false)
                tb.setText(buf);
                tb.toClipboard();
                sheet.paste(range);
            #ENDIF

            buf = "";
        }
        if (cnt >= 65000) break;
    }
    if (cnt < #RowsCount)
        range = sheet.range("A1:J" + int2str(cnt));
    else
        range = sheet.range(strfmt("A%1:J%2",  cnt-(cnt mod #RowsCount)+1, cnt));
    #IF.DDE(true)
        ddeClient.poke(range.Address(true, true, #xlR1C1), buf);
    #ENDIF
    #IF.DDE(false)
        tb.setText(buf);
        tb.toClipboard();
        sheet.paste(range);
    #ENDIF

    excel.visible(true);

    timeFullFinish = timenow();
    timeFullTotal = timeFullFinish - timeFullStart;
    info('Время выполнения, сек');
    info(int2str(timeFullTotal));
}
__________________
Axapta v.3.0 sp5 kr2

Последний раз редактировалось AndyD; 14.06.2006 в 14:39.
За это сообщение автора поблагодарили: konopello (1), Gustav (2), JuniorAx (1).
Старый 14.06.2006, 11:42   #32  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от sukhanchik
2Gustav: Читаю - прям как энциклопедию .... классно. Здорово получилось. (Пока еще раз одобрить не могу) Если выкрою время (в чем сомневаюсь, но можно постараться) - то надо будет еще 2 механизма проверить - через OWC и буфер обмена.
Гы! Спасибо за поддержку. Было бы здорово проверить эти механизмы, а время, думаю, выкроится - мы ж не торопимся особо
Цитата:
Сообщение от AndyD
Еще один вариант - при помощи DDE.
Время выгрузки - 20-21 с
Спасибо! Только чего-то там, мне кажется, не совсем согласовано с проверкой по кол-ву строк - проверьте, пожалуйста, вот эти моменты:
Код:
#define.RowsCount(105000)
..........................
if (cnt > 0 && (cnt mod #RowsCount == 0))
..........................
if (cnt >= 65000) break;
и надо удалить на фиг строку:
Код:
..........................
 
&& ledgerTrans.TransDate >= str2date('dd.mm.yy',123) && ledgerTrans.TransDate <= str2date('DD.MM.YY',123)
..........................
раз уж мы в процессе эксперимента решили прерывать цикл брейком после вывода точно 50 тыс. записей, не заморачиваясь с подбором дат

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

Последний раз редактировалось Gustav; 14.06.2006 в 11:45.
Старый 14.06.2006, 11:59   #33  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
По поводу RowsCount - это для экспериментов: можно запихнуть в Excel все одним запросом, а можно порциями. Это и регулируется параметром RowsCount (вставлял порциями по 5'000 и добавил впереди 10 для загрузки целиком ). Время в общем-то в пределах погрешности, в отличие от варианта с XML - там это тоже используется

TransDate - это оставил из вашего предложения в самом начале топика. На самом деле ограничиваю по RecId
__________________
Axapta v.3.0 sp5 kr2
Старый 14.06.2006, 13:09   #34  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от AndyD
По поводу RowsCount - это для экспериментов: можно запихнуть в Excel все одним запросом, а можно порциями. Это и регулируется параметром RowsCount (вставлял порциями по 5'000 и добавил впереди 10 для загрузки целиком ). Время в общем-то в пределах погрешности, в отличие от варианта с XML - там это тоже используется
Да, порядка 19-20 сек. У меня на всё той же машине (AMD,2.5,4) получилось:
Код:
                               DDE       Clipboard
--------------------------------------------------------
при #define.RowsCount(    1) - 160 сек - 167 сек
при #define.RowsCount(    2) -  96 сек - 109 сек
при #define.RowsCount(    5) -  54 сек -  50 сек
при #define.RowsCount(   10) -  38 сек -  36 сек
при #define.RowsCount(   50) -  24 сек -  24 сек
при #define.RowsCount(  100) -  22 сек -  22 сек
при #define.RowsCount( 1000) -  20 сек -  20 сек
при #define.RowsCount( 5000) -  19 сек -  20 сек
при #define.RowsCount(10000) -  20 сек -  19 сек
при #define.RowsCount(15000) -  19 сек -  19 сек
при #define.RowsCount(20000) -  20 сек -  19 сек
при #define.RowsCount(25000) -  error  -  19 сек
при #define.RowsCount(50000) -  error  -  20 сек
--------------------------------------------------------
error = свалился с ошибкой 
"DDE: A request for a synchronous poke transaction has timed out."
Все результаты приведены для 50 тыс. записей, т.е. if (cnt > 50000) break;

P.S. Пора рисовать графики в логарифмической шкале


P.P.S. добавил в эту же таблицу результаты по варианту "Clipboard" в ответ на:
Цитата:
Сообщение от AndyD
Добавил вариант с Clipboard (точнее - один из вариантов, с помощью TextBuffer.toClipboard()).
| |
V

Последний раз редактировалось Gustav; 14.06.2006 в 20:11.
Старый 14.06.2006, 14:41   #35  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
Добавил вариант с Clipboard (точнее - один из вариантов, с помощью TextBuffer.toClipboard()). Код в сообщении выше.
__________________
Axapta v.3.0 sp5 kr2
Старый 14.06.2006, 17:12   #36  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от AndyD
void LoadKeyboardLayout()
{
dll dll = new dll("user32");
dllFunction dllFunction = new dllFunction(dll, "LoadKeyboardLayoutA");
#define.KLF_ACTIVATE(1)
;
dllFunction.arg(ExtTypes::String, ExtTypes:: DWord);
dllFunction.returns(ExtTypes:: DWord);
dllFunction.call("00000419", #KLF_ACTIVATE);
}
AndyD, попутный вопрос (в плане повышения общей xpp-грамотности)

Это метод что делает? Для чего?

Заранее благодарю за ответ.
Старый 14.06.2006, 17:56   #37  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
Активирует русскую раскладку клавиатуры. Если с помощью toClipboard() копировать при активной английской в WIN2000+, то произойдет ошибка конвертации и вместо русских букв будут крякозяблы. Этот эффект можно наблюдать при копировании из Axapta'ы кириллицы (код Recoilme в этом же топике).

На самом деле есть несколько путей решения этой проблемы - копировать в UNICODE, при вставке в Excel указывать тип вставляемых данных (при помощи метода PasteSpecial на WorkSheet).
__________________
Axapta v.3.0 sp5 kr2
Старый 14.06.2006, 22:23   #38  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
Несколько предложений по исследованию Gustav различных вариантов использования ComExcelDocument_RU

1. При создании нового файла Excel использован код
X++:
excel.newFile("", true, -1);
Замечание по поводу второго параметра _bVisible=true - при массовой вставке данных в лист Excel будет довольно ощутимое замедление выгрузки. Еще одна особенность при таком параметре - если пользователь в листе Excel во время вызгрузи выделит ячейку мышкой, то выполнение прервется с ошибкой.
Первый (самый простой) вариант - открывать в невидимом режиме и показывать после выгрузки всех данных.
Второй вариант - запретить перед экспортом пользовательский ввод и обновления в открытой книге.
X++:
    COM app;
    ;
...
    app = doc.Application();
    app.Interactive(False); // запрет пользовательского ввода
    app.ScreenUpdating(False); // запрет обновления окна при изменении данных
...
    app.Interactive(True);
    app.ScreenUpdating(True);
2. Хотелось бы вступиться за Range . Значительное увеличение времени в первом варианте присходит из-за реализации метода findRange(), используемого в insertValue(). Если заменить этот код на более простой (часть кода взята из второго варианта)
X++:
    COM doc;
    COM actSheet;
    COM range;
...
    doc = excel.getComDocument();
    actSheet = doc.ActiveSheet();
...
    while ()
    {
...
        range = actSheet.range(ComExcelDocument_RU::numToNameCell( 1, row));
        range.Value2(ledgerTrans.RecId);
        range = actSheet.range(ComExcelDocument_RU::numToNameCell( 2, row));
        range.Value2(ledgerTrans.AccountNum);
...
то загрузка хотя и будет происходить медленнее второго способа, но разница будет составлять уже проценты, а не разы.

3. Окончательная реабилитация Range.
Третий вариант с использованием только интерфейсов - вставка при помощи массивов
X++:
    COM actSheet;
    COM range;
    Array arr;
...
    while ()
    {
...
        arr = new Array(Types::String);
        arr.value( 1, strfmt("%1", ledgerTrans.RecId));
        arr.value( 2, strfmt("%1", ledgerTrans.AccountNum));
        arr.value( 3, strfmt("%1", ledgerTable.AccountName));
        arr.value( 4, strfmt("%1", ledgerTable.AccountPlType));
        arr.value( 5, strfmt("%1", ledgerTrans.BondBatchTrans_RU));
        arr.value( 6, strfmt("%1", ledgerTrans.BondBatch_RU));
        arr.value( 7, strfmt("%1", ledgerTrans.TransDate));
        arr.value( 8, strfmt("%1", ledgerTrans.Txt));
        arr.value( 9, strfmt("%1", ledgerTrans.AmountMST));
        arr.value(10, strfmt("%1", ledgerTrans.Crediting));
        range = actSheet.range(strfmt("A%1:J%1", row));
        range.value2(COMVariant::createFromArray(arr));
Этот вариант по сравнению со вторым в моем случае дает увеличение скорости до 7 раз

PS. Упс. Ошибка. Так делать нельзя.
X++:
excel.insertValue(range, ledgerTrans.AccountNum);
На самом деле хотел вставить
X++:
excel.insertValueInRange(range, ledgerTrans.AccountNum);
но этот метод объявлен как protected в стандарте. Если необходимо - можно изменить объявление класса, либо просто вставлять значения так
X++:
range.Value2(ledgerTrans.AccountNum);
__________________
Axapta v.3.0 sp5 kr2

Последний раз редактировалось AndyD; 15.06.2006 в 09:24.
За это сообщение автора поблагодарили: belugin (11).
Старый 14.06.2006, 23:08   #39  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от AndyD
Замечание по поводу второго параметра _bVisible=true - при массовой вставке данных в лист Excel будет довольно ощутимое замедление выгрузки. Еще одна особенность при таком параметре - если пользователь в листе Excel во время вызгрузи выделит ячейку мышкой, то выполнение прервется с ошибкой.
Ну да, да, да, да! Ущучил, блин! Как раз сам собирался покаяться, потому что сегодня смотрел варианты с "SysExcel сотоварищи" и там вспомнил и засек. Да, разница в скорости порядка 10-15 процентов. Об остальном - завтра-послезавтра. С Array - круто! Замерим! Только это не "реабилитация", а, скорее, надо взять всё лучшее из разных подходов и синергически перемножить... Может, к минуте-двум и подтянем такой "ComExcelDocument_RU"

P.S. Собственно говоря, Range-то я и не хаял. Я хаял "заднепроходность" излишнего преобразования номеров строки и колонки в текстовый адрес только для того, чтобы воспользоваться синтаксисом Range("A1") вместо того, чтобы без лишних телодвижений сразу применить что-то вроде Range.Item(1,1)

Последний раз редактировалось Gustav; 14.06.2006 в 23:32.
Старый 15.06.2006, 17:53   #40  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от AndyD
Значительное увеличение времени в первом варианте присходит из-за реализации метода findRange(), используемого в insertValue(). Если заменить этот код на более простой (часть кода взята из второго варианта)
Код:
COM doc;
    COM actSheet;
    COM range;
...
    doc = excel.getComDocument();
    actSheet = doc.ActiveSheet();
...
    while ()
    {
...
        range = actSheet.range(ComExcelDocument_RU::numToNameCell( 1, row));
        range.Value2(ledgerTrans.RecId);
        range = actSheet.range(ComExcelDocument_RU::numToNameCell( 2, row));
        range.Value2(ledgerTrans.AccountNum);
...
то загрузка хотя и будет происходить медленнее второго способа, но разница будет составлять уже проценты, а не разы.
AndyD, всё же несколько слов по поводу "2."

Итак, что мы имеем? Выяснено:
1. Первый способ - джоб SpeedTest_Job1, основанный на использовании методов ComExcelDocument_RU, медленнее, чем SpeedTest_Job2, основанный на COM, примерно в 3 раза.
2. Причиной трёхкратного замедления являются методы класса ComExcelDocument_RU:
- InsertValue (записывает значение в ячейку),
- numToNameCell (вычисляет текстовый адрес ячейки).

То, что "загрузка хотя и будет происходить медленнее второго способа, но разница будет составлять уже проценты, не разы", просто говорит о том, что метод InsertValue (с findRange внутри), пардон, "гадит" на характеристики процесса гораздо больше, чем метод numToNameCell.

На мой взгляд, использование этого комбинированного варианта
Код:
COM range;
.........
range = actSheet.range(ComExcelDocument_RU::numToNameCell( 1, row));
range.Value2(ledgerTrans.RecId);
является полумерой, так как мы устраняем влияние лишь метода InsertValue (т.е. главного виновника), оставляя зачем-то определение всё того же текстового адреса ячейки через numToNameCell - вот и находятся эти самые "уже проценты, а не разы" расхождения.

Но использование Value2 в чистом виде - это уже не совсем класс ComExcelDocument_RU.
Тогда зачем нам оставлять в алгоритме numToNameCell? Сказав "А", скажем и "Б":
Код:
COM cells;
.........
COM::createFromVariant( cells.Item(row, 1) ).Value2( ledgerTrans.RecId );
- тут нам и Value2, и адресация ячейки в виде "номер строки, номер столбца" - как раз то, что нужно в цикле... И уже никакой прямой связи с ComExcelDocument_RU
Теги
benchmark, download, excel, faq, xml, законченный пример, производительность, экспорт/импорт

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Axapta программирует Excel на VBA Gustav DAX: База знаний и проекты 10 13.03.2006 11:42
Использование OWC.Spreadsheet для ускорения экспорта/импорта в/из Excel. storer DAX: Программирование 24 28.03.2005 19:10
Передача данных из 1С в Axapta 3.0 через COM Connector isbist DAX: Программирование 10 03.12.2004 10:58
Особенности экспорта данных в Excel Roman-sp DAX: Функционал 18 01.03.2004 12:07
Введение в Аксапту Роман Кошелев DAX: Прочие вопросы 0 18.12.2001 14:00

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 10:19.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.