小甲:老师,我最近很郁闷,你作为国内的ABAP一哥,给我指条路吧。
老白:咋了小子?上来就送一顶高帽,你且说来听听。
小甲:我不是在做ABAP报表优化嘛,本来一切都很顺利,直到有一个大量使用了长文本的报表,卡壳了。这个报表不仅要取很多长文本数据,而且还要根据里面的值做筛选,我想不到有什么好办法优化了。
老白:原来是这样啊,那你是怎么取的长文本呢?
小甲:就是使用READ_TEXT挨个订单号取啊,难道还有其他的办法吗?
老白:READ_TEXT是取少量长文本使用的,你要是有大量长文本需要取数就不能使用这个函数了,你还记得我给你说过数据库取数相关的优化有个原则是啥不?
小甲:老师我知道你要说啥,不就是减少IO嘛?可是长文本使用了数据簇的方式存储,没有办法批量直接在DB取出来,只能一个一个取数呀。
老白:是的,你说的方向的对的,只是不知道如何批量取数而已。(转头面对观众)实际上呢,READ_TEXT把数据抬头存到表STXH,使用的是正常的存储方式,而真正的长文本内容在表STXL,这个就是数据簇的方式了,也就是读写都比较特殊,需要用
EXPORT xxxx TO DATABASE xxx ( xx) ID...., IMPORT xxxx FROM DATABASE
xxx(xx) ID....这种方式去写入或者读取,而且这个不支持批量读取,(转回头)也就是你说的只能一个一个取数了。
小甲:是的呀老师,这不就废了嘛,一条无法逾越的鸿沟挡住了优化的道路,我可咋办啊,嘤嘤嘤。。。
老白:你看你怎么还哭上了,我给你说过的ABAP优化第一原则是啥你还记得不?
小甲:记得,第一原则就是ABAP优化没有尽头,只要你想,总归有方法的。不过我感觉这次是到了尽头了,没得吹了,标准的不支持咋办呢?
老白:数据簇的DB格式不是不支持批量读取,只是没有直接的语法支持而已,我们可以通过变通的方法达到我们的目的,你还记得有个IMPORT xxxx FROM
INTERNAL TABLE... 的语法不?
小甲:没有用过哎,不过看上去这个是在内表取数的,跟DB不是一回事啊,不过既然你都问我这个了,那肯定就是用它变通了呗。
老白:对头,孺子可教!你可以先把数据批量在STXL取到内表,然后使用 IMPORT xxxx FROM INTERNAL
TABLE如此这般这般一通操作,就行了。
小甲:?什么这般那般的?怎么就行了?老师你不要给我打哑谜啊,用一两句话给我简简单单讲明白讲透彻就行了。
老白:一两句话要是能讲明白讲透彻我还逼逼这么多干嘛,还是看代码吧。(面对观众)下面代码是利用销售订单的长文本来做一个优化方案和标准取数的对比,直接复制到色38就可以执行看看效果了,有一点要注意哈,其他老师也有讲这个办法的,但是大多都忽略了两个重点,一个是STXL的数据可能有多行,也就是STXL-
SRTF2大于0的情况,另一个是如果是复制的长文本,也就是STXH-TDREF = 'X'的时候,需要使用 TDREFOBJ、 TDREFNAME、
TDREFID去STXL取数,另外如果你的系统比较新,可以看看有没有函数READ_TEXT_TABLE,这个函数也可以批量读取,原理是一样的。
*&---------------------------------------------------------------------**& 对比两种方式读取长文本的性能*& Baitianzhen **&---------------------------------------------------------------------*REPORT zlong_text NO STANDARD PAGE HEADING.
TABLES:vbak.DATA: BEGIN OF gt_ltext OCCURS 0, tdobject TYPE stxh-tdobject, tdname TYPE stxh-tdname, tdid TYPE stxh-tdid, tdspras TYPE stxh-tdspras, tlines TYPE string, END OF gt_ltext.DATA: gt_ltext2 LIKE gt_ltext[] WITH HEADER LINE.DATA: t1 TYPE i, t2 TYPE i.DATA: gt_vbeln TYPE TABLE OF vbeln WITH HEADER LINE.
SELECT-OPTIONS: s_vbeln FOR vbak-vbeln, s_erdat FOR vbak-erdat DEFAULT sy-datum.START-OF-SELECTION. SELECT vbeln INTO TABLE gt_vbeln FROM vbak WHERE vbeln IN s_vbeln AND erdat IN s_erdat. WRITE: '订单条数:',sy-dbcnt. LOOP AT gt_vbeln. gt_ltext-tdobject = 'VBBK'. gt_ltext-tdname = gt_vbeln. gt_ltext-tdid = '0001'. gt_ltext-tdspras = '1'. APPEND gt_ltext. gt_ltext-tdobject = 'VBBK'. gt_ltext-tdname = gt_vbeln. gt_ltext-tdid = '0002'. gt_ltext-tdspras = '1'. APPEND gt_ltext. ENDLOOP. gt_ltext2[] = gt_ltext[].
GET RUN TIME FIELD t1. PERFORM get_long_text_fm TABLES gt_ltext. GET RUN TIME FIELD t2. t1 = t2 - t1. WRITE: / '使用标准函数所用时间:', t1.
GET RUN TIME FIELD t1. PERFORM get_long_text_im TABLES gt_ltext2. GET RUN TIME FIELD t2. t1 = t2 - t1. WRITE: / '使用优化方案所用时间:', t1.
*&---------------------------------------------------------------------**& 使用标准函数的方式读取*&---------------------------------------------------------------------*FORM get_long_text_fm TABLES t_ltext STRUCTURE gt_ltext. DATA lt_tline TYPE TABLE OF tline WITH HEADER LINE.
CHECK t_ltext[] IS NOT INITIAL. SELECT tdobject tdname tdid tdspras INTO TABLE t_ltext FROM stxh FOR ALL ENTRIES IN t_ltext WHERE tdobject = t_ltext-tdobject AND tdid = t_ltext-tdid AND tdspras = t_ltext-tdspras AND tdname = t_ltext-tdname. IF sy-subrc = 0. LOOP AT t_ltext. CLEAR lt_tline[]. CALL FUNCTION 'READ_TEXT' EXPORTING id = t_ltext-tdid language = t_ltext-tdspras name = t_ltext-tdname object = t_ltext-tdobject TABLES lines = lt_tline EXCEPTIONS OTHERS = 8. LOOP AT lt_tline. CONCATENATE t_ltext-tlines lt_tline-tdline INTO t_ltext-tlines. ENDLOOP. IF t_ltext-tlines IS INITIAL. DELETE t_ltext. CONTINUE. ELSE. MODIFY t_ltext. ENDIF. ENDLOOP. ELSE. CLEAR t_ltext[]. ENDIF.ENDFORM. "get_long_text_fm*&---------------------------------------------------------------------**& 优化方案*&---------------------------------------------------------------------*FORM get_long_text_im TABLES t_ltext STRUCTURE gt_ltext. DATA: BEGIN OF lt_stxh OCCURS 0, tdobject TYPE stxh-tdobject , tdname TYPE stxh-tdname , tdid TYPE stxh-tdid , tdspras TYPE stxh-tdspras , tdrefobj TYPE stxh-tdrefobj , tdrefname TYPE stxh-tdrefname, tdrefid TYPE stxh-tdrefid , END OF lt_stxh. DATA: BEGIN OF lt_key OCCURS 0, tdobject TYPE stxl-tdobject, tdname TYPE stxl-tdname, tdid TYPE stxl-tdid, tdspras TYPE stxl-tdspras, END OF lt_key. DATA: BEGIN OF lt_stxl OCCURS 0, tdobject TYPE stxl-tdobject, tdid TYPE stxl-tdid, tdname TYPE stxl-tdname, tdspras TYPE stxl-tdspras, srtf2 TYPE stxl-srtf2 , clustr TYPE stxl-clustr, clustd TYPE stxl-clustd, END OF lt_stxl. DATA: BEGIN OF lt_clust OCCURS 0, clustr TYPE stxl-clustr, clustd TYPE stxl-clustd, END OF lt_clust. DATA lt_tline TYPE TABLE OF tline WITH HEADER LINE.
CHECK t_ltext[] IS NOT INITIAL. SELECT tdobject tdname tdid tdspras tdrefobj tdrefname tdrefid INTO TABLE lt_stxh FROM stxh FOR ALL ENTRIES IN t_ltext WHERE tdobject = t_ltext-tdobject AND tdid = t_ltext-tdid AND tdspras = t_ltext-tdspras AND tdname = t_ltext-tdname. IF sy-subrc = 0. LOOP AT lt_stxh. IF lt_stxh-tdrefname = ''. lt_key-tdobject = lt_stxh-tdobject. lt_key-tdname = lt_stxh-tdname. lt_key-tdid = lt_stxh-tdid. ELSE. lt_key-tdobject = lt_stxh-tdrefobj . lt_key-tdname = lt_stxh-tdrefname. lt_key-tdid = lt_stxh-tdrefid . ENDIF. lt_key-tdspras = lt_stxh-tdspras. COLLECT lt_key. ENDLOOP.
CHECK lt_key[] IS NOT INITIAL. SELECT tdobject tdid tdname tdspras srtf2 clustr clustd INTO TABLE lt_stxl FROM stxl FOR ALL ENTRIES IN lt_key WHERE relid = 'TX' AND tdobject = lt_key-tdobject AND tdid = lt_key-tdid AND tdname = lt_key-tdname AND tdspras = lt_key-tdspras. SORT lt_stxl BY tdobject tdid tdname srtf2. CLEAR t_ltext[].
LOOP AT lt_stxh. CLEAR: lt_clust[],lt_tline[]. IF lt_stxh-tdrefname = ''. READ TABLE lt_stxl WITH KEY tdobject = lt_stxh-tdobject tdid = lt_stxh-tdid tdname = lt_stxh-tdname tdspras = lt_stxh-tdspras BINARY SEARCH. IF sy-subrc = 0. LOOP AT lt_stxl FROM sy-tabix. IF lt_stxl-tdobject NE lt_stxh-tdobject OR lt_stxl-tdid NE lt_stxh-tdid OR lt_stxl-tdname NE lt_stxh-tdname OR lt_stxl-tdspras NE lt_stxh-tdspras. EXIT. ENDIF.
lt_clust-clustr = lt_stxl-clustr. lt_clust-clustd = lt_stxl-clustd. APPEND lt_clust. ENDLOOP. IMPORT tline = lt_tline FROM INTERNAL TABLE lt_clust. ENDIF. ELSE. READ TABLE lt_stxl WITH KEY tdobject = lt_stxh-tdrefobj tdid = lt_stxh-tdrefid tdname = lt_stxh-tdrefname tdspras = lt_stxh-tdspras BINARY SEARCH. IF sy-subrc = 0. LOOP AT lt_stxl FROM sy-tabix. IF lt_stxl-tdname NE lt_stxh-tdrefname OR lt_stxl-tdobject NE lt_stxh-tdrefobj OR lt_stxl-tdid NE lt_stxh-tdrefid OR lt_stxl-tdspras NE lt_stxh-tdspras. EXIT. ENDIF.
lt_clust-clustr = lt_stxl-clustr. lt_clust-clustd = lt_stxl-clustd. APPEND lt_clust. ENDLOOP. IMPORT tline = lt_tline FROM INTERNAL TABLE lt_clust. ELSE. CALL FUNCTION 'READ_TEXT' EXPORTING id = lt_stxh-tdrefid language = lt_stxh-tdspras name = lt_stxh-tdrefname object = lt_stxh-tdrefobj TABLES lines = lt_tline EXCEPTIONS OTHERS = 8. ENDIF. ENDIF.
t_ltext-tdobject = lt_stxh...