Подход к реализации больших форматированных отчетов в SAP BW

от автора

На проектах внедрения отчетности с использованием хранилища данных SAP BW многим архитекторам и консультантам приходится решать задачи подготовки больших форматированных отчетов: разнообразных ведомостей, выписок и т.п. Такие отчеты обычно характеризуются:

  • Нестандартными относительно инструментов SAP требованиями к форматированию;
  • Фиксированным числом столбцов;
  • Значительным количеством столбцов и строк (соответственно, десятки и десятки тысяч и более);
  • Требованием наличия Excel-представления;
  • Требованием к времени выполнения не более нескольких минут

К сожалению, нередко приходится наблюдать ситуацию, когда архитекторы BW-проектов выбирают стандартный для BW подход реализации таких отчетов. Кратко суть этого подхода изложена ниже.

Консультантом создается рабочая книга BW-BEx, которая содержит один или несколько BW-BEx-отчетов. Отчеты выгружаются на отдельные листы этой книги, которые обычно скрывают от пользователей. Видимым оставляют лишь один лист книги, содержащий целевую форму отчета с необходимым форматированием.

Работа пользователя с таким отчетом выглядит следующим образом:

  • в зависимости от используемого Excel-инструмента SAP BW, пользователь запускает BW-BEx Analyzer или SBOP Analysis for Office, подключается к серверу SAP BW, выбирает из роли рабочую книгу и запускает ее на выполнение.
    Через несколько секунд (иногда – десятка секунд) появляется селекционный экран.
    На экране пользователь выбирает значения параметров. Например, год-месяц, балансовую единицу, группу материала и т.п. Затем нажимает кнопку «выполнить».
  • Теперь настала очередь «поработать» для SAP BW: все BW-BEx-отчеты рабочей книги выполняются последовательно, отчет за отчетом, передавая на рабочие листы Excel свои данные.
  • После получения в Excel данных каждого отчета запускается VBA-макрос. Логика работы макроса такова, что он ничего не делает, пока данные всех отчетов не будут получены на Excel-листы.
  • Когда данные последнего отчета поступили на Excel-лист, VBA-макрос выполняет основную работу по подготовке форматирования отчета.
  • Когда VBA-макрос завершил работу, пользователь может увидеть результат отчета в своем Excel.

У стандартного подхода есть ряд преимуществ: он прост в реализации и им хорошо владеют большинство специалистов на рынке. Но определенные ограничения не позволяют эффективно реализовывать большие отчеты. А неэффективная реализация получается (если вообще получается) очень неудобной в работе, что негативно сказывается на отношении пользователей к проекту внедрения вообще и к SAP BW в частности. Основное ограничение – максимальное количество ячеек (число строк, умноженное на число столбцов) в отчете. Если их число приближается к эмпирическим 750000, то вероятность сбоя из-за нехватки памяти практически 100%. Т.е. отчет из всего 18 колонок и чуть более 40000 строк уже попадает под это ограничение. А ведь лимиты у Excel намного больше.

Чего только не придумывают консультанты, чтобы, оставаясь в рамках стандартного подхода, качественно сделать-таки большой отчет. Но почти всегда ничего не получается. «Почти» означает компромиссы, послабления в требованиях. Бизнес-пользователи либо соглашаются применять более ограничивающие фильтры и отчет возвращает меньше данных, либо ждать выполнения подольше, либо вручную сводить несколько фрагментов отчета в один.

Чтобы все-таки не говорить клиенту «нет, мы не можем этого реализовать при таких требованиях», необходимо для начала сделать правильные выводы из очевидного: каждый инструмент предназначен для своей задачи.

Инструменты BW BEx Analyzer и SBOP Analysis for Office в общем случае не предназначены для реализации эффективных отчетов с большим количеством ячеек, с числом около 750000 и более (см SAP-ноту 1040454). Поэтому, используя модель данных SAP BW, надо выбрать другой инструмент, другой подход в реализации. Тогда решение не только обязательно получится, но и будет при этом эффективным.

Последние версии SAP Netweaver, SAP BW и HANA внесли большее разнообразие подходов публикации BW-данных в Excel, без использования BW BEx. Можно упомянуть такие:

  • Подключение Excel через OData-сервисы напрямую к SAP Netweaver или даже к SAP HANA
  • Подключение Excel к SAP HANA, как к базе данных, напрямую, через MDX

Однако, эти подходы требуют либо BW on HANA, либо новейших версий Excel, либо отклонений от привычных концепций полномочий, при которой пользователи не работают с приложениями, обращающимися напрямую к БД.

Я хочу рассказать о подходе, гораздо менее требовательном к новизне версий используемых продуктов, и в чем-то менее сложным. Речь идет о публикации данных отчетов в шаблон Excel-документа через OLE-интерфейс. Excel-шаблон при этом хранится в репозитории BDS на стороне SAP BW.

Преимущества подхода с OLE очевидны:

  • Работает на любых современных версиях продуктов SAP и Microsoft Excel
  • Никаких ограничений на объемы данных в отчете, кроме собственных в Excel
  • Обеспечивает максимальную производительность передачи данных от сервера BW в Excel через OLE. Пример: выборка 525000 ячеек (70 колонок на 7500 строк) передается за 7 сек.
  • Подготовка данных на «сервере BW» выполняется в ABAP-отчете, который, собрав выборку во внутреннюю таблицу, передает ее через OLE в Excel-шаблон, полученный из BDS.
  • Централизованное (в одной BW-системе) ведение всех объектов, релевантных для отчета: шаблон Excel, модель данных BW, программа ABAP для заполнения шаблона.
  • Соответствие обычным SAP-стандартам по разграничению доступа, разработке, транспорту настроек и пр.

«Обратная сторона» медали – этот подход требует программирования на ABAP. Но, по мнению автора, этот аспект не должен вызывать существенных трудностей. «Обертка» из вызова Excel-файла из BDS, его заполнения данными и сохранения, например, в файл на диске или обратно в BDS — более менее стандартный код, который с минимальными вариациями используется от отчета к отчету.

Сложности в ABAP могут возникнуть при получении данных из модели BW. Возможные варианты: вызов BEx-отчета в ABAP, вызов FM RSDRI_INFOPROV_READ, SQL-SELECT по таблицам модели данных. Но это обычно есть в арсенале навыков опытного BW-консультанта. Глубокие знания программировании ABAP понадобятся, если возникнет потребность еще более ускорить работу кода по подготовке данных за счет тюнинга ABAP-программы или даже распараллеливания вычислений. Последнее, кстати, невозможно архитектурно в подходе с рабочими книгами BW BEx.

Вкратце, порядок создания отчета с использованием подхода с OLE следующий.

  • Разработка и отладка кода ABAP, который возвращает во внутреннюю таблицу данных отчета в соответствии с входными параметрами. ABAP-код может быть в виде FM, а лучше – в виде статического метода ABAP-класса;
  • Подготовка Excel-шаблона отчета с базовым форматированием и vBA-макросом, который выполняется после заполнения данными. Такой макрос обычно принимает параметр «число строк», хотя и это не обязательно. Задачи макроса – применить форматирование ячеек отчета при условии неизвестного наперед количества строк;
  • Помещение Excel-шаблона в репозиторий BDS;
  • Разработка и отладка ABAP-кода, который заполняет внутреннюю таблицу результатов отчета, считывает из BDS Excel-шаблон, помещает в него данные из внутренней таблицы в соответствии с мэппингом «поле в таблице – поле в шаблоне», запускает на выполнение VBA-макрос, сохраняет заполненный файл на диске во временном каталоге и открывает его на просмотр пользователю;
  • Подготовка пользовательской транзакции, которая готовится на основе разработки из предыдущего пункта.

Что может понадобится, чтобы сделать первый пример на основе подхода с ABAP-OLE и успешно применять его в дальнейшем?

  • Транзакция работы с репозиторием BDS: OAOR
  • Фрагменты ABAP-кода по работе с документами из BDS (см ниже)
  • Фрагменты ABAP-кода по работе с Excel через OLE (см. ниже)
  • Сертификат для макроса VBA или разрешающая опция Excel по запуску макросов (см. support.microsoft.com/en-us/kb/206637)

data: l_iref_template    type ref to cl_bds_document_set,         l_oref_container   type ref to cl_gui_custom_container,         l_iref_control     type ref to i_oi_container_control,         l_iref_error       type ref to i_oi_error,         l_iref_document    type ref to i_oi_document_proxy,         l_iref_spreadsheet type ref to i_oi_spreadsheet,         l_retcode          type soi_ret_string.    data: lt_signature type sbdst_signature,         lw_signature type bapisignat,         lt_uri       type sbdst_uri,         lw_uri       type bapiuri,         lt_sheet     type soi_sheets_table,         lw_sheet     type soi_sheets.    data: lt_fields   type standard table of rfc_fields,         lv_last_row type i,         lv_last_col type i. call method c_oi_container_control_creator=>get_container_control     importing       control = l_iref_control       retcode = l_retcode.    check l_retcode = c_oi_errors=>ret_ok.    call method l_iref_control->init_control     exporting       r3_application_name      = pv_template       inplace_enabled          = 'X'       inplace_scroll_documents = 'X'       parent                   = l_oref_container     importing       retcode                  = l_retcode.    check l_retcode = c_oi_errors=>ret_ok.    create object l_iref_template.   lw_signature-prop_name  = 'DESCRIPTION'.   lw_signature-prop_value = pv_template.   append lw_signature to lt_signature.    refresh lt_uri.   call method l_iref_template->get_with_url     exporting       classname       = 'SOFFICEINTEGRATION'       classtype       = 'OT'       object_key      = 'SOFFICEINTEGRATION'     changing       uris            = lt_uri       signature       = lt_signature     exceptions       nothing_found   = 1       error_kpro      = 2       internal_error  = 3       parameter_error = 4       not_authorized  = 5       not_allowed     = 6.    clear lw_uri.   read table lt_uri into lw_uri index 1.   check sy-subrc = 0.    call method l_iref_control->get_document_proxy     exporting       document_type  = 'Excel.Sheet'     importing       document_proxy = l_iref_document       retcode        = l_retcode.    check l_retcode = c_oi_errors=>ret_ok.    call method l_iref_document->open_document     exporting       document_url = lw_uri-uri       open_inplace = 'X'     importing       retcode      = l_retcode.    check l_retcode = c_oi_errors=>ret_ok.    free l_iref_error.   call method l_iref_document->get_spreadsheet_interface     importing       error           = l_iref_error       sheet_interface = l_iref_spreadsheet.    call method l_iref_spreadsheet->get_sheets     importing       sheets = lt_sheet       error  = l_iref_error.    check l_iref_error->error_code = c_oi_errors=>ret_ok.    clear lw_sheet.   read table lt_sheet into lw_sheet index 1.   check sy-subrc = 0.    call method l_iref_spreadsheet->select_sheet     exporting       name  = lw_sheet-sheet_name     importing       error = l_iref_error.    check l_iref_error->error_code = c_oi_errors=>ret_ok.    refresh lt_fields.   call function 'DP_GET_FIELDS_FROM_TABLE'     tables       data   = pt_excel       fields = lt_fields.    lv_last_row = lines( pt_excel ).   lv_last_col = lines( lt_fields ).    call method l_iref_spreadsheet->set_selection     exporting       left    = 1       top     = 2       rows    = lv_last_row       columns = lv_last_col.    call method l_iref_spreadsheet->insert_range     exporting       columns = lv_last_col       rows    = lv_last_row       name    = pv_template.    call method l_iref_spreadsheet->insert_one_table     exporting       data_table   = pt_excel[]       fields_table = lt_fields       rangename    = pv_template. …    call method l_iref_document->execute_macro     exporting       macro_string = 'Module1.MakeFormat' *     no_flush     = ' '       param1       = 10       param_count  = 1     importing       error        = l_iref_error       retcode      = l_retcode …   concatenate  pv_file sy-uzeit '.xls' into pv_file.   call method l_iref_document->save_as     exporting       file_name = pv_file.    call method l_iref_document->release_document     importing       retcode = l_retcode.    free: l_iref_spreadsheet,         l_iref_document.    call method l_iref_control->release_all_documents.   call method l_iref_control->destroy_control.  create object lo_application 'Excel.Application'.     if sy-subrc <> 0.       write: / 'Create application: RETURN CODE ='(I10), sy-subrc.       exit.     endif.     call method of lo_application 'Workbooks' = lo_workbooks.     if sy-subrc <> 0.       write: / 'Application->Workbooks: RETURN CODE ='(I10), sy-subrc.       exit.     endif.     call method of lo_workbooks 'Open' = lo_workbook exporting #1 = lv_filename.     if sy-subrc <> 0.       write: / 'Workpook->Open: RETURN CODE ='(I10), sy-subrc.       exit.     endif.     set property of lo_application  'Visible' = 1.     call method of lo_application 'Run' exporting #1 = 'Module1.MakeFormat' #2 = lv_last_row.     if sy-subrc <> 0.       write: / 'Macro run: RETURN CODE ='(I10), sy-subrc.       exit.     endif. 

ссылка на оригинал статьи http://habrahabr.ru/post/269675/


Комментарии

Добавить комментарий

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