文章目录
前言
笔者应届生入职甲方ABAP工程师,乙方离项之后随着SAP的使用数据量增多,有一个自开发程序ZMM007库龄库存查询的报表运行10几分钟都不能出来,领导要求优化这个程序,经过排查最耗时长的逻辑代码是一个双层loop循环,按照数据量的计算需要循环将近40亿次。所以我们要想办法在这块动动手脚优化一下。
一、如何优化双层循环?
我们要做的就是减少不必要的循环次数,内层LOOP循环是使用了where去循环筛选出符合条件的数据,这样每次都要把内层循环的数据从头到尾都需要循环一遍,这样就比较浪费时间了,我们要做的就是让它只循环指定区间符合范围的数据,这样就可减少不必要循环的次数。
二、优化前我们需要了解的知识点
1.如何循环内表指定区间的数据?
a.
LOOP的时候可以使用FROM
从指定行开始循环
示例代码
****** 以下几行代码是取数逻辑可以不用看 *****
REPORT z_test8.
TYPES: BEGIN OF ty_data.
INCLUDE STRUCTURE zstudent_lk_01.
TYPES:END OF ty_data.
DATA gt_data TYPE TABLE OF ty_data.
SELECT
*
FROM zstudent_lk_01
INTO CORRESPONDING FIELDS OF TABLE gt_data.
SORT gt_data by zcode.
******主要看以下代码就行上面是取数而已*****
LOOP AT gt_data FROM 5 INTO DATA(test)."指定这个内表从第 5 行开始循环
WRITE: / test-zname ,test-zcode.
WRITE: /.
ENDLOOP.
运行效果
b.
上面的例子循环的区间其实是从指定起始位置到末尾,但实际我们还可以将区间再缩小可以使用EXIT
关键字当数据不符合我们条件的时候就退出循环,这样我们就可以只循环符合数据的行来最大化减少循环次数了,但是使用EXIT
必须将循环的表进行排序,如果不排序那么数据就不是连续的我们也就无法使用EXIT进行判断退出循环了,如果是排完序的数据我们就可以从起始位置进行每一次循环判断如果遇到不符合的数据就直接退出循环,这样也就证明我们符合的数据也就循环完了。例如上面的数据我们要只循环体重等于55的人
示例代码
SORT gt_data BY weigh."首先必须按照体重排序,这样才能将符合的数据集中到一起方便我们去连续判断是否将所有符合数据循环完了
READ TABLE gt_data TRANSPORTING NO FIELDS WITH KEY weigh = 55."找到起始符合条件行的索引
IF sy-subrc = 0."读取成功的话将起始索引保存,开始从起始索引循环,读取失败就不进行循环。相当于跳过此次内层循环
DATA(get_index) = sy-tabix."当有表读取成功后 sy-tabix 这个变量会返回读取成功那行的索引
LOOP AT gt_data FROM get_index INTO DATA(test)."从符合数据起始行的位置开始循环
IF test-weigh <> 55."如果遇到不符合数据就直接跳出循环,因为我们之前排过序这样可以保证我们将符合的数据都循环完
EXIT.
ENDIF.
WRITE: / test-zname ,test-zcode,test-weigh.
WRITE: /.
ENDLOOP.
ENDIF.
运行效果
实际我们使用loop where关键字循环符合条件的数据也可以达到这样的效果,但是给LOOP加where条件程序还是会从头到尾将数据循环一遍只不过数据量小的时候我们察觉不到而已,上面代码这个关键字 TRANSPORTING NO FIELDS 的意思是不读取到任何结构中,意思就是只读取只想看读取成功了没有,并不需要将读取成功的值赋值给某个变量。
2.项目案例
a.
首先我们先看一下调优前的代码,笔者大概计算了一下段双层循环最终要执行循环次数是40亿次。因为内外循环的表数据量都非常大所以导致程序运行速度很慢,我们需要使用上面所掌握的知识来将代码优化,内层循环我们只循环符合的区间数据。
LOOP AT gt_output ASSIGNING FIELD-SYMBOL(<fs_out>)."外层循环最终要展示的数据
LOOP AT lt_mch1 INTO DATA(lw_mch1) WHERE matnr = <fs_out>-matnr
AND charg = <fs_out>-charg."这是内层循环,使用where循环符合条件的数据
IF lw_mch1-atinn = '0000000813'. "Z_BBH
<fs_out>-zbms = lw_mch1-atwrt.
ELSEIF lw_mch1-atinn = '0000000810'."Z_LEVEL "左边这点判断逻辑可以不用看,笔者也不懂是干什么。
<fs_out>-zdj = lw_mch1-atwrt.
ELSEIF lw_mch1-atinn = '0000000811'."Z_LOTNO
<fs_out>-zscph = lw_mch1-atwrt.
ENDIF.
MOVE-CORRESPONDING lw_mch1 TO <fs_out>."将内层循环符合条件的数据字段值添加到外层循环的内表当前行中去
ENDLOOP.
ENDLOOP.
b.
调优后的代码,首先拿到外层循环的第一行数据根据关键字段去查找内层循环表中匹配符合的数据,如果找到就返回拿到索引,如果没有的话就直接跳出内层循环,直接去拿外层循环表中的第二行数据,再根据第二行数据的关键字段去内层循环表中读取符合数据行的索引,如果读取成功内层循环就循环表中符合数据的区间。一次类推直到将外层循环表中的每一行数据都在内层循环的表中进行了符合数据的区间循环。
SORT lt_mch1 BY matnr charg."将内层循环的表按照查找的关键字进行排序,这个是必须的,不然会漏掉数据。
LOOP AT gt_output ASSIGNING FIELD-SYMBOL(<fs_out>).
READ TABLE lt_mch1 TRANSPORTING NO FIELDS WITH KEY matnr = <fs_out>-matnr "使用二分查找 根据关键字读取起始符合数据行
charg = <fs_out>-charg BINARY SEARCH."排序的数据才可以使用二分查找
IF sy-subrc = 0.
DATA(find_index) = sy-tabix."起始符合数据行的索引
LOOP AT lt_mch1 FROM find_index INTO DATA(lw_mch1)."从符合数据的起始行开始循环
IF <fs_out>-matnr <> lw_mch1-matnr OR <fs_out>-charg <> lw_mch1-charg."向下循环的过程遇到不符合数据的行就跳出循环
EXIT.
ENDIF.
IF lw_mch1-atinn = '0000000813'. "Z_BBH
<fs_out>-zbms = lw_mch1-atwrt.
ELSEIF lw_mch1-atinn = '0000000810'."Z_LEVEL
<fs_out>-zdj = lw_mch1-atwrt.
ELSEIF lw_mch1-atinn = '0000000811'."Z_LOTNO
<fs_out>-zscph = lw_mch1-atwrt.
ENDIF.
MOVE-CORRESPONDING lw_mch1 TO <fs_out>.
ENDLOOP.
ENDIF.
ENDLOOP.
c.
需要注意的点
笔者第一次优化的时候在写 遇到不符合数据的行跳出循环 这段逻辑代码的时候只使用了 matnr 去判断下一行数据是否符合而导致数据循环添重复了,最终我发现问题后加上了 charg 字段但是又是用AND关键字去连接的,导致数据还是会循环添加重复,实际我们应该按照OR去连接条件这样才可以达到我们查询的需求。意思就是如果有一个关键字段不匹配就跳出循环,但是要是用AND连接意思就是所有关键字段都不匹配才跳出循环,可见这明显是不符合我们逻辑的,只有用OR连接才可以查询出数据到底是不是符合的。
三、像这种情况的其他解决方式
1.使用排序表,这个解决方法是看了其他大神的文章
LOOP AT WHERE 语句在 SORTED TABLE 上会自动进行 BINARY SEARCH ,而在 STANDARD TABLE 上则不会。因此,如果 LT_MCH1 是一个 STANDARD TABLE ,那么调优后的代码会比原来的代码更快
SORTED TABLE = 排序表
STANDARD TABLE = 标准表
BINARY SEARCH = 二分法搜索
我自己认为还是循环表的区间应该快一点,因为尽管排序表loop where的时候会使用二分法搜索,但是如果有5条数据需要找出来那么它应该就需要执行5次二分法搜索了,而循环区间的话我们等于直接告诉它从这里循环到哪里就是我们需要的数据并不需要一遍一遍的去找。个人看法不知道想的对不对。
四、关于补充点
1.
a.
b.
2.
a.
b.
3.如果我想起了有什么要补充的会继续写在这里,或者大家有什么想让我介绍的也可以评论私信我哦。
五、总结
以上就是今天要讲的内容,本文仅仅简单介绍了sap中ABAP代码优化的一个小点,如果有说错或者不好的地方还望大家提出来见谅。感觉笔者写的好的别忘了关注点赞加评论哦,也欢迎大家一起来讨论。谢谢!