读取SAP CRM One Order应用日志的优化方式
Recently I am responsible for the performance optimization of one API which retrieves application logs of the given one order documents.
API Requirement
The input is an internal table containing document guids, and output are all the application logs belonging to those documents, with type “Error” and priority “Very important” and “Important”.
For the application logs of a single document, you can view them via tcode SLG1 as below:
The application logs would be displayed in WebUI as below:
The signature of API:
The structure of table type CRMT_ODATA_TASK_LOGST could be found below:
Original implementation ( has performance issue )
The idea of original implementation is:
LOOP AT it_order_guid_tab.
" get all of the message handles of the current order by its guid and stores to variable lt_message_handles
LOOP AT lt_message_handles.
" get the detail of each message according to its handle
ENDLOOP.
ENDLOOP.
As you see there are nested loop, so the algorithm complexity is o(n2).
The optimized implementation
The idea is to avoid the nested LOOP.
" get all message handles belonging to all orders in a single method call
lt_all_message_handlers = get_all( it_order_guid_tab ).
LOOP AT lt_all_message_handlers.
" get the detail of each message according to its handle
ENDLOOP.
The algorithm complexity is reduced to o(n). The original implementation could be found from method GET_ORDER_ERROR_MESSAGE of the class CL_CRM_ORDER_MESSAGE_TOOL in the attachment. The optimized version is in method GET_ORDER_ERROR_MESSAGE_OPT.
Performance comparison
I do the performance measurement based on the following three scenarios. We can see the performance is improved a lot after nested LOOP is removed.
The idea of original implementation is:
Unit Test
In order to guarantee that the optimized implementation does return exactly the same data as the original one, I write the following report to do unit test. The design is quite simple, retrieve the application log for the same input twice, one using the original implementation and the other one using the optimized version, and eASSERT lt_result1 = lt_result2. The report source code is attached here:
REPORT tool_display_log_compare.
CLASS lcl_test DEFINITION.
PUBLIC SECTION.
METHODS: test_all_oppt, test_created_by_jerry, test_oppt_jerry,
constructor.
PRIVATE SECTION.
METHODS: get_oppt_guid, compare, get_created_by, get_oppt_jerry.
DATA: mt_guid_tab TYPE crmt_object_guid_tab,
mt_msg_origin TYPE crmt_odata_task_logst,
mt_msg_opt LIKE mt_msg_origin,
mo_tool TYPE REF TO cl_crm_order_message_tool.
ENDCLASS.
CLASS lcl_test IMPLEMENTATION.
METHOD: test_oppt_jerry.
get_oppt_jerry( ).
compare( ).
WRITE: / 'lines of message: ' , lines( mt_msg_origin ).
WRITE: / 'test on all Opportunity with type OPPT and created by Jerry passed.' COLOR COL_NEGATIVE.
ENDMETHOD.
METHOD: test_created_by_jerry.
get_created_by( ).
compare( ).
WRITE: / 'lines of message: ' , lines( mt_msg_origin ).
WRITE: / 'test on all Opportunity created by Jerry passed.' COLOR COL_NEGATIVE.
ENDMETHOD.
METHOD: test_all_oppt.
get_oppt_guid( ).
compare( ).
WRITE: / 'lines of message: ' , lines( mt_msg_origin ).
WRITE: / 'test on all Opportunity with type OPPT passed.' COLOR COL_NEGATIVE.
ENDMETHOD.
METHOD: get_created_by.
CLEAR: mt_guid_tab.
SELECT guid INTO TABLE mt_guid_tab FROM crmd_orderadm_h WHERE created_by = 'WANGJER'.
ENDMETHOD.
METHOD: get_oppt_jerry.
CLEAR: mt_guid_tab.
SELECT guid INTO TABLE mt_guid_tab FROM crmd_orderadm_h WHERE process_type = 'OPPT' AND created_by = 'WANGJER'.
ENDMETHOD.
METHOD: get_oppt_guid.
CLEAR: mt_guid_tab.
SELECT guid INTO TABLE mt_guid_tab FROM crmd_orderadm_h WHERE process_type = 'OPPT'.
ENDMETHOD.
METHOD: compare.
CLEAR: mt_msg_origin, mt_msg_opt.
mt_msg_origin = mo_tool->get_order_error_message_opt( mt_guid_tab ).
CALL FUNCTION 'CRM_MESSAGES_INIT'
EXPORTING
it_docnumber = mt_guid_tab.
mt_msg_opt = mo_tool->get_order_error_message( mt_guid_tab ).
SORT mt_msg_origin BY header_guid log_msg.
SORT mt_msg_opt BY header_guid log_msg.
ASSERT mt_msg_origin = mt_msg_opt.
ENDMETHOD.
METHOD: constructor.
mo_tool = NEW cl_crm_order_message_tool( ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: lo_test TYPE REF TO lcl_test.
lo_test = new lcl_test( ).
lo_test->test_all_oppt( ).
lo_test->test_created_by_jerry( ).
lo_test->test_oppt_jerry( ).
- 2000! | 看上去如此简单的面试题,让太多“前端”英雄好汉折戟
- 【Windows编程】系列第六篇:创建Toolbar与Statusbar
- arguments,想说爱你不容易
- 【android开发】Android binder学习一:主要概念
- 高考啦! JavaScript高考全国卷
- 2017 JavaScript高考全国卷 参考答案与解析
- 用贝叶斯判别分析方法预测股票涨跌
- 开发 | 在 Mac OS X 装不上 TensorFlow?看了这篇就会装
- 【答疑解惑】Java中的默认构造器和equals方法
- 原生JS | 当兔子遇到鸡
- 【Android基础】Activity的生命周期函数
- 七种常用回归技术,如何正确选择回归模型?
- 爬取拉勾网大数据相关岗位薪资信息存到excel,并作数据分析
- 【Windows编程】系列第五篇:GDI图形绘制
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法