《ABAP:SAP报表的性能优化》由会员分享,可在线阅读,更多相关《ABAP:SAP报表的性能优化(6页珍藏版)》请在金锄头文库上搜索。
1、大部分ABAPer都是从SAP报表及打印开始学起的,大家也都认为写个SAP报表程序是最简单不过的事 了。但是实际情况真的如此吗?写报表时除了保证数据的准确性,您可曾考虑过报表的性能问题吗?由于报表程序是被最多SAP用户所访问的,所以性能差的报表很可能会引来大量的抱怨和质疑,大大降低 用户满意度。最近做了较多性能优化方面的工作,颇有感触,在此进行归纳总结,希望对大家有所帮助,也欢迎大家讨 论。1,关于表连接语句(INNER JOIN, LEFT JOIN.)写报表的时候,表与表之间的关联是不可避免的。通常而言,表连接语句要掌握的原则有:(1) 将最有效的查询条件所对应的表放在第一位。换言之,让查
2、询第一个表后所得到的结果集就尽可能小。 比如有一张报表叫做订单状态统计表,可能查完VBAK、VBAP后还查询LIPS、VTTP等,界面上的查询 条件很多。不过据了解得知,该报表主要是查看昨天创建或前几天创建的订单。那么最有效的限制条件自 然是订单创建日期,以及销售组织了,而表连接的主表宜选用VBAK。(2) 确定了表连接的次序后,应考虑将查询条件尽量限制在靠前的表里。比如选择屏幕上有个物料号的查 询条件,而我们知道订单VBAP和交货单LIPS均有物料号,那就应该视情况而定,如果表连接中VBAP 更早出现,那么WHERE子句中就使用vbapmatnr IN s_matnr,反之就是lipsmat
3、nr IN s_matnr。(3) 两个表之间进行连接的时候,应考虑关键字段或索引字段的作用。比如查询VTTP和LIPS时,关联 关系是vttpvbeln = lipsvbeln。那么vttp在前,lips在后,就会比较快,因为根据vttp的vbeln 查询lips时,vbeln是lips的关键字段,速度较快。而反过来如果lips在前,那根据lipsvbeln查询vttp 会慢一些,除非vbeln是vttp的索引字段。2,先构建RANGE再执行SQL语句有时我们所能用的查询条件不是很理想,比如查询LIKP却必须用公司代码,而非销售组织。此时普通做法是用LIKP与TVKO根据VKORG进行表联接
4、,从而限制TVKOBUKRS的值。还有一种有效的办法是,通过TVKO查询到当期公司代码所对应的全部销售组织,从而组建一个RANGE 出来,再根据此RANGE查询LIKP。当然要注意RANGE的行项目有上限的,在ECC6中大概2万行将 导致 ABAP DUMP。提示:DATA r_vkorg TYPE RANGE OF likp-vkorg.SIGN = I, OPTION = EQ, LOW = XXXX即可往 r_vkorg 中放入多个单值。3,使用COLLECT的注意事项这里把COLLECT单独拎出来,并非排斥对它的使用。事实上COLLECT语句非常好用而且功能强大,却 也因此往往被滥用。
5、COLLECT 一般是在LOOP循环内被使用,进行汇总运算。对于每个COLLECT wa INTO it_sum语句,系统会 先查找内表it_sum中是否存在相同属性的条目,如果存在则在此条目上对数值进行叠加;如果不存在则新 增一个条目。所以,这里涉及了查找的过程,而且该查找动作是在LOOP内进行的,是不是有点类似于嵌套 循环?不过,ABAP早考虑到此因素。查看帮助发现,原来此查找过程采用了哈希法,怪不得程序性能依然 很好。但是,笔者曾经碰到过一个很慢的报表,仅仅通过对COLLECT语句进行调整,即大大优化了其性能。这是 因为该报表没有考虑到以下2点:(1) it_sum中的非数值字段不应过多
6、。上面说了,系统采用哈希法查找it_sum是否存在相同属性的条目。 如果非数值字段过多,哈希法的性能将因运算量过大而急速下降。(2) it_sum的最终条目数应该少点。如果条目数增长过快,则查找过程的速度可能将受影响,从而影响 性能。比如COLLECT vbak-vkorg可能比COLLECT vbak-vbeln会快些,因为前者的重复性高,结果集的条 目数少。4, For All Entries 与 Select Single 的比较就个人而言,笔者不是很喜欢For All Entries语句,因为它的缺点多于优点。很多人都会说,为什么呀, For All Entries不是比Select
7、Single快么?事实到底怎样呢,让我们做个比较。假设我们有个内表代表销售订单的行项目,该内表有10万行。此时我们要根据LIPS的VGBEL和VGPOS, 查询这些订单行项目对应的交货单行项目。如果用SELECT SINGLE的话写法很简单:LOOP AT it_vbap INTO wa_vbap.SELECT SINGLE vbelnposn rFROM lips INTO (wa_vbap-vbeln_vl, wa_vbap-pos nr_vl)WHERE vgbel = wa_vbap-vbeln AND vgpos = wa_vbap-pos nr AND vgtyp = C.MODI
8、FY it_vbap FROM wa_vbap.ENDLOOP.如果用For All Entries则写法分2步:SELECT vbelnpos nrFROM lips INTO TABLE it_lipsFor All Entries IN it_vbapWHERE vgbel = it_vbap-vbeln ANDvgpos = it_vbap-pos nr ANDvgtyp =C.此交货单是根据订单创建的LOOP AT it_vbap INTO wa_vbap.READ TABLE it_lips INTO wa_lips WITH KEY vgbel = wa_vbap-vbelnvg
9、pos = wa_vbap-vgpos.IF sy-sub rc = 0.wa_vbap-vbeln_vl = wa_lips-vbeln.wa_vbap-pos nr_vl = wa_lips-pos nr.MODIFY it_vbap FROM wa_vbap.ENDIF.ENDLOOP.对于SELECT SINGLE而言,由于LIPS有个VGB的SAP自带索引,每次查询都挺快,即便循环10万 次,速度虽然快不了但也没什么大的危害。对于For All Entries的第一步,的确比SELECT SINGLE快些,本来10万次的SELECT SINGLE,变 成了 1万次的查询(如果BASI
10、S设置了参数为10),每次查询10个订单行项目。但是第二步就很慢了,暂 估it_lips为8万行,则对于it_vbap的10万个行项目,有8万个行项目执行READ TABLE语句平均需 要4万次才能搜索到it_lips的目标行,另有2万个行项目将读遍it_lips然后才发现没有对应的目标行。 所以我们一共需要搜索48亿次,太慢了!在此针对For All Entries的使用提出几点意见:(1)如果是根据某数据量大的内表用For All Entries读取数据量小的配置表,比如TVAK/T006等,那 不如把For All Entries直接去掉,把表里的几十条数据全部取出。(2)使用For A
11、ll Entries时,SELECT语句后面的字段必须包含所查表关键字段。比如上面的 vbeln/posnr就是lips的关键字段。如果不含关键字段,比如SELECT lfimg FROM lips For All Entries *,那么当LIPS中两个条目关键字段不同而lfimg相同时,会被SAP自动过滤掉一条。(3)上面关于性能问题,应该利用BINARY SEARCH、SORTED TABLE或者HASHED TABLE来解决。 详见下面第四节。5,关于 BINARY SEARCH/SORTED TABLE/HASHED TABLE 的使用BINARY SEARCH即二分法查找,在保证内
12、表按查询字段以升序排列的时候,可以采用二分法查找。二分 法查找的速度很快,最大查询次数为logy。 以上面的例子来说,如果it_lips事先按vgbel和vgpos排 好序,则每次查找最多不超过17次。则对于it_vbap的10万个行项目,仅100多万次就可以搞定了! 当使用READ TABLE语法时,如果查询字段跟SORTED TABLE的排序开始字段能匹配上,则SAP将自 动采用二分法查找。比如it_lips是SORTED TABLE且以vgbel和vgpos排序,则当read table以vgbel 进行查找时,系统会自动采用二分法。但如果read table以vgpos和其他字段进行查
13、找,由于vgpos并 非SORTED TABLE的第一排序字段,系统将采用直线查找,速度会慢很多。总之,SORTED TABLE的 排序字段次序也很关键。针对STANDARD TABLE排序后可以进行二分法查找,使用SORTED TABLE也可进行二分法查找,那 么二者有什么区别呢?简单来说,由于SORTED TABLE自始至终都保持排序,如果需要对内部进行频繁 的插入、删除操作,则不推荐使用SORTED TABLE,性能会很差。而另一方面,如果我们要用的是类似 LOOP AT itips WHERE vgbel = * 的语法(而非 READ TABLE)时,对于 STANDARD TABL
14、E 就 无法采用二分法查找,或者说会很麻烦。而对于SORTED TABLE,系统会自动采用二分法优化查找过程。至于HASHED TABLE,笔者用得也不太多。查看SAP的标准代码,貌似用得也没很多。理论上HASHED TABLE可以比SORTED TABLE更快些,但需要耗用更大的存储空间。当某程序采用了二分法查找之后, 如果效果还不是很理想,建议可以用HASHED TABLE试试。6,将循环内的重复性工作进行缓冲有时我们的内表数据量很大,但又不得不在每次循环的时候,都进行类似的一些操作,比如调用函数 FI_PERIOD_DETERMINE获取某日期对应的会计年度和期间,调用函数MD_CONV
15、ERT_MATERIAL_UNIT进行单位转换,等等。每个函数的调用背后都要执行一系列的读表以 及运算工作,程序的效率明显下降了。所以我们得想出有效的办法。比如针对FI_PERIOD_DETERMINE的调用,可以改用循环前对函数G_PERIODS_OF_YEAR_GET的 调用。根据公司代码BUKRS读取T001-PERIV,然后调用G_PERIODS_OF_YEAR_GET获取某会计 年度每个会计期间对应的起始日期和结束日期。这样在循环内部,只要根据上面的结果即可算出某日期对 应的会计年度和期间了。又比如针对MD_CONVERT_MATERIAL_UNIT的调用。相信对于it_vbap的1
16、0万个行项目,物料号重 复的有很多。所以可以先汇总物料号,然后一次性读取表MARM以存储换算关系。有了 MARM的换算关 系,循环中大量的单位换算就可以自己算了,如果无法换算的再考虑调用函数MD_CONVERT_MATERIAL_UNIT。(函数 MD_CONVERT_MATERIAL_UNIT 除了读取 MARM 的换算关系,还会考虑同一维度单位间的换算关系比如G和KG的关系,所以其功能更强大。)7,关于字段的增强以及TABLE INDEX的创建这里提到字段的增强,主要是性能方面相关的。假设我们需要基于系统所有的billing document做个动 作,比如将其导出到金税系统。至少有两种方案:第一是新建一个表,专门记录已经导出到金税的开票凭 证;第二是在系统标准的开票凭证抬头表