AXForum  
Вернуться   AXForum > Блоги > Gustav'ово бложище, или Записки DAX-дилетанта-III
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

Стараюсь писать про Аксапту, хотя частенько тянет в Офис
Рейтинг: 3.67. Голосов: 6.

Построение сводной таблицы на форме с загрузкой данных из ADODB.Recordset

Запись от Gustav размещена 17.05.2010 в 19:00

Сразу определимся с терминами. "Сводная таблица на форме" - это Office Web Component PivotTable, размещенный как элемент управления ActiveX на форме Аксапты. "ADODB.Recordset" - в первую (и наиболее интересную для нас) очередь отсоединенный (или неприсоединенный, или, если угодно, disconnected), в общем, не связанный с базой данных набор записей, формируемый программистом в оперативной памяти (процесс во многом напоминает заполнение массива: Вывод в Excel через Array). Работа с отсоединенным ADODB.Recordset подробно описана в теме Поговорим об ADO, а также в ряде других смежных обсуждений (Строка в Excel, экспорт в шаблон excel и т.п.).

До недавнего времени я не знал о существовании возможности подачи данных в OWC PivotTable из отсоединенного Recordset'а, будучи, тем не менее, в курсе подобной возможности для сводной таблицы в Excel через PivoteCache (Вывод в Excel сводной таблицы "пользователи-группы"). В том своем сообщении я очень сожалел об отсутствии объекта PivotCache у OWC PivotTable. И думал, что данные могут поступать только из Recordset'ов, связанных с реальными таблицами БД.

Оказалась, что возможность загрузки из неприсоединенного Recordset'а всё-таки существует! Не в том смысле, что нашёлся PivotCache у PivotTable - его как не было, так и нет - и это на сегодняшний день медицинский факт. Но в том смысле, что загружать данные всё-таки можно, хотя и несколько идеологически иначе - при помощи метода PivotTable.DataSource( recordset ): см. Сводные таблицы и Olap в Dax2009.

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

Джоб решает по сути абсолютно ту же задачу, что и джоб из сообщения Вывод в Excel сводной таблицы "пользователи-группы". Поэтому оба листинга полезно сравнить буквально пооператорно, определяя моменты сходства и отличия между "большим" Excel и "маленьким" OWC. Очень интересен в этом отношении способ гашения итогов: при визуальной схожести фрагментов "Subtotals(1, false)" они обнаружили заметное различие по смыслу. В Excel первый параметр - одно из значений перечисления: 1-Automatic, 2-Sum, 3-Count,..., 11-Var,12-Varp. В OWC PivotTable - "Must be 1" (см.комментарий и ссылку в коде).
X++:
#CCADO
static void Job_showUserGroupInActiveXPivot(Args _args)
{
    Form                form = new Form();
    FormBuildDesign     formBuildDesign = form.addDesign('Design');
    Args                args = new Args();
    FormRun             formRun;
    FormActiveXControl  pivotTable;

    UserGroupList       userGroupList;
    UserInfo            userInfo;
    UserGroupInfo       userGroupInfo;

    COM                 rst,flds,fld;   // переменные ADO

    COM                 ptConstants;    // переменные PivotTable
    COM                 activeView;
    COM                 avFieldSets, fieldSet;
    COM                 pivotFields, pivotField, pivotTotal;
    COM                 comTemp;

    void processFieldSet(COM _axis, str _fieldSetName)
    {
        fieldSet = avFieldSets.Item(_fieldSetName);
        _axis.InsertFieldSet(fieldSet);     // добавляем FieldSet в ось (т.е в Строки или в Столбцы)

        pivotFields = fieldSet.Fields();    // в семействе полей FieldSet'а...
        pivotField = pivotFields.Item(0);   // ...находим единственное поле... (индексация начинается с 0)
        pivotField.Subtotals(1, false);     // ...и гасим (false) для него промежуточные итоги
        // по поводу 1 см. "Must be 1" в [url]http://msdn.microsoft.com/en-us/library/aa831714(office.10).aspx[/url]
    }
    ;

// готовим рекордсет
    rst = AdoRst::openRecordsetInMemory([   // метод можно взять здесь: [url]http://www.axforum.info/forums/blog.php?b=60[/url]
            ['UserId'    , #adVarChar,  5 ],
            ['UserName'  , #adVarChar, 40 ],
            ['GroupId'   , #adVarChar, 10 ],
            ['GroupName' , #adVarChar, 40]]);

    flds = rst.Fields();

    while select userGroupList
        join userInfo
            where userInfo.id == userGroupList.userId
        join userGroupInfo
            where userGroupInfo.id == userGroupList.groupId
    {
        rst.AddNew();
            fld = flds.Item('UserId'   ); fld.Value(userGroupList.userId );
            fld = flds.Item('UserName' ); fld.Value(userInfo.name        );
            fld = flds.Item('GroupId'  ); fld.Value(userGroupList.groupId);
            fld = flds.Item('GroupName'); fld.Value(userGroupInfo.name   );
        rst.Update();
    }

// динамически генерируем форму
    args.object(form);

    formRun = classFactory.formRunClass(args);
    formRun.init();
    formRun.design().caption('Сводная таблица "Пользователи-Группы"');

    pivotTable = formRun.design().addControl(FormControlType::ActiveX, 'PivotTable');
    pivotTable.className('{0002E542-0000-0000-C000-000000000046}'); // Microsoft Office PivotTable 10.0

    pivotTable.heightMode(FormHeight::ColumnHeight);    // именно heightMode ! не путать с просто height
    pivotTable.widthMode(FormWidth::ColumnWidth);       // именно widthMode  ! не путать с просто width
    pivotTable.AutoFit(false);

    ptConstants = pivotTable.Constants(); // для использования ниже именованной константы plFunctionCount

// передаем рекордсет ActiveX'у
    pivotTable.DataSource(rst);


    activeView = pivotTable.ActiveView();
    avFieldSets = activeView.FieldSets(); // список доступных полей рекордсета для размещения в сводной таблице

// сокрытие разных "визуальных раздражителей" (здесь хорошо помог \Classes\GM_PivotViewManager\ initDefaultViewProperties от GMCS)
    COM::createFromObject( pivotTable.ActiveData() ).HideDetails();     // это лучше сделать до размещения полей и особенно итогов - иначе летаргически долго
    COM::createFromObject( activeView.TitleBar()   ).Visible(false);    // выключение синего заголовка "Сводная таблица MS Office 10.0"
    comTemp = ActiveView.FilterAxis();
    COM::createFromObject( comTemp.Label() ).Visible(false);            // выключение области "Перетащите сюда поля фильтра"
    pivotTable.AllowDetails(false);                                     // чтобы убрались "+/-" и не получать надпись "Нет деталей"
    pivotTable.DisplayOfficeLogo(false);                                // выключение самой левой красно-сине-желто-зеленой кнопки на панели


// наполняем строки полями UserName и UserId
    processFieldSet(activeView.RowAxis(), 'UserName');
    processFieldSet(activeView.RowAxis(), 'UserId');

// наполняем столбцы полями GroupId и GroupName
    processFieldSet(activeView.ColumnAxis(), 'GroupId');
    processFieldSet(activeView.ColumnAxis(), 'GroupName');

// наполняем область данных итогом - подсчетом кол-ва по полю GroupName (это поле осталось в переменной pivotField после его вставки в столбцы)
    pivotTotal = activeView.AddTotal('Количество', pivotField, ptConstants.plFunctionCount());  // использование именованной константы
    COM::createFromObject( activeView.DataAxis() ).InsertTotal(pivotTotal);

    formRun.run();
    formRun.wait();
}
При подготовке этого сообщения я использовал следующие материалы для изучения вопроса и написания джоба:
  • файл помощи OWCVBA10.CHM на своем компьютере, а также другие источники, описанные в сообщении Сводные таблицы и Olap в Dax2009;
  • демонстрационный пример Ivanhoe из сообщения Сводные таблицы и Olap в Dax2009;
  • наработки компании GMCS, которые нашёл в AOT нашей корпоративной Аксапты (3.0, SP4) - классы и формы с именами, начинающимися на "GM_Pivot"; пример работающей реализации этого хозяйства можно увидеть (клиентам GMCS!) по маршруту: Управление запасами \Запросы \Оборотно-сальдовая ведомость по складу \вкладка Обзор \кнопка Сводная таблица);
  • Метод \Classes\SysTableLookup\formRun - для подсматривания простейшего способа создания динамической формы без использования класса Dialog.
Размещено в Без категории
Просмотров 237046 Комментарии 1
Всего комментариев 1

Комментарии

  1. Старый комментарий
    Мне кажется метод не коректно отрабатывает, хотя я могу ошибаться.
    Когда вы сделали сводную таблицу и нажимаете на кнопку "экспорт в эксель" у вас происходит выгрузка? у меня вылезает ошибка
    DAX 2009

    Я потратил много времени, чтобы выяснить что именно не так.
    Надо добавить строку
    X++:
    #define.adAffectAll(3)
    ...
    rst.UpdateBatch(#adAffectAll);
    Запись от refined размещена 31.03.2011 в 16:11 refined is offline
    Обновил(-а) refined 31.03.2011 в 18:25
 


Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 00:35.