SAP ABAP 长文本取数的终极优化

小甲:老师,我最近很郁闷,你作为国内的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-tdobject.
      t_ltext-tdname   = lt_stxh-tdname  .
      t_ltext-tdid     = lt_stxh-tdid    .
      t_ltext-tdspras  = lt_stxh-tdspras .
      LOOP AT lt_tline.
        CONCATENATE t_ltext-tlines lt_tline-tdline INTO t_ltext-tlines.
      ENDLOOP.
      CHECK t_ltext-tlines IS NOT INITIAL.
      APPEND t_ltext. CLEAR t_ltext.
    ENDLOOP.
  ELSE.
    CLEAR t_ltext[].
  ENDIF.
ENDFORM.                    "get_long_text_im

小甲:老师,结果出来了!提升了大概3到4倍的效率,感觉虽然还行,但不是特别的惊艳啊,没有达到那种让人情不自禁“哇塞”一声是效果哈,这种提升效果不符合你的身份哦!

5c2291c70d3964accc09a11cc2877876.png

老白:你小子还学会激将法了?看来不给你来点狠活是不行了,你知道江湖上的一句传言不?

小甲:?

老白:那就是著名的一类优化方法:用空间换时间!具体来说呢,就是把需要的数据按照需要的格式整理好放到表里面,等需要的时候直接根据主键来拿就行了,速度那是非常的快啊。

小甲:老师你这么一说我茅厕顿开了!SAP的信息结构(Information structures),就像S022,S039这种表,也是这个思路,我当初还做过好多个呢!那玩意确实好用,牺牲有点磁盘空间就能获取取数的极大快感,值!不过长文本似乎没法做信息结构啊

老白:还茅厕顿开,你还是赶紧把茅厕关上吧,味儿都传出来了啊喂!那是茅塞顿开!

小甲:老师你就别纠结细节了,赶紧说说咋办吧!

老白:当然是用增强咯,当然你要是觉着无所谓直接改源码也行。首先创建一个表来存放长文本数据,这个表的关键是存放长文本的字段,这个字段的定义非常有讲究,如果是定义短了呢,稍微长点的文本内容就放不下,要是太长呢,S4支持最大30000,一篇论文都能放得下了,但是如果你的系统是ECC,仅仅能支持到1333长度,那肯定不大够。如果是定义成STRING呢,倒是不用担心长度问题了,但是在SQL的SELECT中又不能用来限制数据的读取了,所以,如果是S4,直接定义一个长的字段就妥了,如果是ECC就麻烦很多,建议可以像下面这样定义:

5f363e7cfc0997df0f72c46de8763f2d.png

小甲:这样挺好老师,兼顾了所有长度文本的存储和大多数长文本的SQL取数限制需求,而且还有长度字段,可以知道这个TLINE是不是只存了一部分内容。要是S4,把TLINE定义好长好长,TLSTR就不需要了。

老白:是的,下面是增强程序的修改,需要修改函数COMMIT_TEXT

e1116cddf442f78987563f91137a3a66.png

DATA: ls_zltxt TYPE zltxt.
  LOOP AT catalog WHERE tdobject IN r_object
                  AND   tdname   IN r_name
                  AND   tdid     IN r_id
                  AND   tdspras  IN r_language.


    DELETE FROM zltxt
      WHERE tdobject = catalog-tdobject AND
            tdname   = catalog-tdname   AND
            tdid     = catalog-tdid     AND
            tdspras  = catalog-tdspras.
    CHECK catalog-function NE function_delete.


    memory_id+8(6) = catalog-id.
    CLEAR clines[].
    IMPORT tline TO clines FROM MEMORY ID memory_id.
    CHECK sy-subrc = 0.


    CLEAR ls_zltxt.
    ls_zltxt-tdobject = catalog-tdobject.
    ls_zltxt-tdname   = catalog-tdname.
    ls_zltxt-tdid     = catalog-tdid.
    ls_zltxt-tdspras  = catalog-tdspras.
    LOOP AT clines.
      CONCATENATE ls_zltxt-tlstr clines-tdline INTO ls_zltxt-tlstr.
    ENDLOOP.
    ls_zltxt-tsize    = strlen( ls_zltxt-tlstr ).
    ls_zltxt-tline    = ls_zltxt-tlstr.
    INSERT zltxt FROM ls_zltxt.
  ENDLOOP.

小甲:老师你好大的胆子呀,不是说不让直接修改源码吗?说修改了源码系统升级的时候容易给覆盖掉,还得做SPAU。

老白:年轻人,胆子可以大一点嘛,只要你完全知道你做的事儿对系统有啥影响,也可以适当改改源码,不过最推荐的办法还是增强哈,或者隐式增强。

小甲:Get!我弄点测试数据看看是不是已经存到表里了哈。哦也,果然可以了!a6392e2e3a6b683bde49808bcec63b92.png

老白:是的,这下你就可以直接JOIN这个表来取数了,是不是特别爽?

小甲:那是相当的爽啊,速度要提升几百倍啦!这点磁盘空间用的值!不过老师,似乎还有点问题呢,,,

老白:?

小甲:第一、我不想每个类型的长文本都存储,因为我用不到,白白占用磁盘空间,还拖慢了存取数的速度。第二、TDNAME这个字段是KEY字段的组合,在S4可以用concat关键字查询,ECC不行啊。第三、历史数据咋办呢?增强又不会把历史数据弄进去?

老白:第一个问题好办,弄一个配置表就行了,需要保存的OBJECT维护到表里面,在增强里面判断一下再保存。第二个问题有点棘手,如果你对性能极度渴望呢,就再新建你需要主键的表,增强保存的拆分KEY数据放到这个新表,如果性能要求不是那么极端呢,用FOR ALL ENTRIES也是可以的。至于第三个,肯定得有一个配套的初始化/检查程序嘛。

小甲:老师牛逼!这下又可以愉快的写着代码唱着歌,哈哈哈。另外,我发现还有两个微信群可以扫码加入,想讨论的小伙伴可以加入哈。

84e2d5e86965978fe3ee112ae9ab4541.png7f772368434bfc44bfeab4c0b18cca2d.png

初始化以及检查程序如下:

dbf38f7e5e01fbddb9aaac17c44ea009.png

*&---------------------------------------------------------------------*
*& Report  ZLTXT_INIT
*&
*&---------------------------------------------------------------------*
*& 长文本增强工具程序
*& Baitianzhen 
*&---------------------------------------------------------------------*
REPORT zltxt_init NO STANDARD PAGE HEADING LINE-SIZE 1023.


TABLES stxh.
DATA: gt_fldct TYPE lvc_t_fcat,
      gs_slayt TYPE lvc_s_layo,
      gs_varnt TYPE disvariant,
      gv_repid TYPE sy-repid.
DATA: gt_stxh TYPE TABLE OF zltxt WITH HEADER LINE.
DATA: gt_ltxt TYPE TABLE OF zltxt WITH HEADER LINE.
DATA: gt_line TYPE TABLE OF tline WITH HEADER LINE.


SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE btxt2.
PARAMETERS: pr_init RADIOBUTTON GROUP g1 USER-COMMAND r1.
PARAMETERS: pr_chck RADIOBUTTON GROUP g1 DEFAULT 'X'.
PARAMETERS: pr_disp RADIOBUTTON GROUP g1.
SELECTION-SCREEN END OF BLOCK b2.


SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE btxt1.
SELECT-OPTIONS: s_obj   FOR stxh-tdobject DEFAULT 'VBBK',
                s_id    FOR stxh-tdid    ,
                s_spras FOR stxh-tdspras ,
                s_name  FOR stxh-tdname  ,
                s_date  FOR stxh-tdfdate  MODIF ID b,
                s_size  FOR gt_ltxt-tsize MODIF ID a,
                s_line  FOR gt_ltxt-tline MODIF ID a.
SELECTION-SCREEN END OF BLOCK b1.


AT SELECTION-SCREEN OUTPUT.
  btxt1 = '数据筛选'.
  btxt2 = '功能'.
  %_s_obj_%_app_%-text   = '应用程序对象'.
  %_s_id_%_app_%-text    = '文本标识'.
  %_s_spras_%_app_%-text = '语言'.
  %_s_name_%_app_%-text  = '名称'.
  %_s_date_%_app_%-text  = '创建日期'.
  %_s_size_%_app_%-text  = '文本长度'.
  %_s_line_%_app_%-text  = '文本内容(前132字符)'.
  %_pr_init_%_app_%-text = '初始化'.
  %_pr_chck_%_app_%-text = '检查数据'.
  %_pr_disp_%_app_%-text = '显示ZLTXT表内容'.
  LOOP AT SCREEN.
    CASE 'X'.
      WHEN pr_init OR pr_chck.
        IF screen-group1 CA 'A'.
          screen-active = '0'.
        ENDIF.
      WHEN OTHERS.
        IF screen-group1 CA 'B'.
          screen-active = '0'.
        ENDIF.
    ENDCASE.
    MODIFY SCREEN.
  ENDLOOP.


START-OF-SELECTION.
  CASE 'X'.
    WHEN pr_init OR pr_chck.
      PERFORM get_stxh_data.
    WHEN pr_disp.
      PERFORM show_zltxt.
      PERFORM outdata.
  ENDCASE.


*&---------------------------------------------------------------------*
*&      Form  get_stxh_data
*&---------------------------------------------------------------------*
FORM get_stxh_data.
  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  ,
          tdfdate   TYPE stxh-tdfdate  ,
        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: lv_cursor TYPE cursor,
        lv_dbcntr TYPE i.


  OPEN CURSOR WITH HOLD lv_cursor FOR
    SELECT tdobject tdname tdid tdspras
           tdrefobj tdrefname tdrefid tdfdate
     FROM stxh
     WHERE tdobject IN s_obj   AND
           tdid     IN s_id    AND
           tdspras  IN s_spras AND
           tdname   IN s_name  AND
           tdfdate  IN s_date.
  DO.
    FETCH NEXT CURSOR lv_cursor INTO TABLE lt_stxh PACKAGE SIZE 10000.
    IF sy-subrc NE 0.
      EXIT.
    ENDIF.
    lv_dbcntr = sy-dbcnt.


    CLEAR: lt_key[],lt_stxl[],gt_stxh[],gt_stxh.
    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 tdspras srtf2.


    LOOP AT lt_stxh.
      CLEAR: lt_clust[],gt_line[],gt_line.
      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 = gt_line 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 = gt_line 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    = gt_line
            EXCEPTIONS
              OTHERS   = 8.
        ENDIF.
      ENDIF.


      gt_stxh-tdobject = lt_stxh-tdobject.
      gt_stxh-tdname   = lt_stxh-tdname  .
      gt_stxh-tdid     = lt_stxh-tdid    .
      gt_stxh-tdspras  = lt_stxh-tdspras .
      LOOP AT gt_line.
        CONCATENATE gt_stxh-tlstr gt_line-tdline INTO gt_stxh-tlstr.
      ENDLOOP.
      gt_stxh-tsize    = strlen( gt_stxh-tlstr ).
      gt_stxh-tline    = gt_stxh-tlstr.
      CHECK gt_stxh-tline IS NOT INITIAL.
      APPEND gt_stxh. CLEAR gt_stxh.
    ENDLOOP.


    CHECK gt_stxh[] IS NOT INITIAL.
    CASE 'X'.
      WHEN pr_init.
        MODIFY zltxt FROM TABLE gt_stxh.
      WHEN pr_chck.
        PERFORM check_data.
    ENDCASE.
    CALL FUNCTION 'DB_COMMIT'.
    MESSAGE s000(oo) WITH '累计处理完条数:' lv_dbcntr.
  ENDDO.
  CLOSE CURSOR lv_cursor.
ENDFORM.                    "get_stxh_data


*&---------------------------------------------------------------------*
*&      Form  CHECK_DATA
*&---------------------------------------------------------------------*
FORM check_data.
  CLEAR gt_ltxt[].
  SELECT * INTO TABLE gt_ltxt
   FROM zltxt
   FOR ALL ENTRIES IN gt_stxh
   WHERE tdobject = gt_stxh-tdobject AND
         tdid     = gt_stxh-tdid     AND
         tdspras  = gt_stxh-tdspras  AND
         tdname   = gt_stxh-tdname.


  SORT gt_stxh BY tdobject tdname tdid tdspras.
  SORT gt_ltxt BY tdobject tdname tdid tdspras.


  SKIP.
  LOOP AT gt_stxh.
    READ TABLE gt_ltxt WITH KEY tdobject = gt_stxh-tdobject
                                tdname   = gt_stxh-tdname
                                tdid     = gt_stxh-tdid
                                tdspras  = gt_stxh-tdspras
                                BINARY SEARCH.
    IF sy-subrc = 0.
      IF gt_stxh-tlstr = gt_ltxt-tlstr.
        DELETE gt_ltxt INDEX sy-tabix.
        DELETE gt_stxh.
        CONTINUE.
      ELSE.
        WRITE: / '应该:',gt_stxh-tdobject,gt_stxh-tdid,
                 gt_stxh-tdname,gt_stxh-tlstr.
        WRITE: / '现值:',gt_ltxt-tdobject,gt_ltxt-tdid,
                 gt_ltxt-tdname,gt_ltxt-tlstr.
      ENDIF.
    ENDIF.
  ENDLOOP.


  SKIP.
  LOOP AT gt_stxh FROM 1 TO 10.
    WRITE: / '缺少:',gt_stxh-tdobject,gt_stxh-tdid,gt_stxh-tdname,
             gt_stxh-tdspras, AT 92 gt_stxh-tlstr.
  ENDLOOP.
ENDFORM.                    " CHECK_DATA


*&---------------------------------------------------------------------*
*&      Form  show_zltxt
*&---------------------------------------------------------------------*
FORM show_zltxt.
  SELECT * INTO TABLE gt_ltxt FROM zltxt
    WHERE tdobject IN s_obj   AND
          tdid     IN s_id    AND
          tdspras  IN s_spras AND
          tdname   IN s_name  AND
          tsize    IN s_size  AND
          tline    IN s_line.
ENDFORM.                    "show_zltxt


*&---------------------------------------------------------------------*
*&      Form  outdata
*&---------------------------------------------------------------------*
FORM outdata.
  gv_repid            = sy-repid.
  gs_slayt-zebra      = 'X'.
  gs_slayt-detailinit = 'X'.
  gs_slayt-cwidth_opt = 'X'.
  gs_varnt-report     = sy-repid.
  gs_varnt-handle     = 1.


  CHECK gt_ltxt[] IS NOT INITIAL.
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
    EXPORTING
      i_structure_name        = 'ZLTXT'
      i_save                  = 'A'
      is_variant              = gs_varnt
      is_layout_lvc           = gs_slayt
      i_callback_program      = gv_repid
      i_callback_user_command = 'USER_COMMAND'
    TABLES
      t_outtab                = gt_ltxt.
ENDFORM. " outdata


*&--------------------------------------------------------------------*
*&      Form  user_command
*&--------------------------------------------------------------------*
FORM user_command USING pv_ucomm TYPE sy-ucomm
                        pv_field TYPE slis_selfield.
*  DATA lt_text TYPE catsxt_longtext_itab.
  DATA lt_text TYPE TABLE OF tdline WITH HEADER LINE.


  READ TABLE gt_ltxt INDEX pv_field-tabindex.
  CASE pv_ucomm.
    WHEN '&IC1'.
      CASE pv_field-fieldname.
        WHEN 'TLSTR' OR 'TLINE'.
          CALL FUNCTION 'READ_TEXT'
            EXPORTING
              id       = gt_ltxt-tdid
              language = gt_ltxt-tdspras
              name     = gt_ltxt-tdname
              object   = gt_ltxt-tdobject
            TABLES
              lines    = gt_line
            EXCEPTIONS
              OTHERS   = 8.
          IF sy-subrc NE 0.
            MESSAGE ID sy-msgid TYPE 'E' NUMBER sy-msgno
                    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
          ELSE.
            LOOP AT gt_line.
              APPEND gt_line-tdline TO lt_text.
            ENDLOOP.
            CALL FUNCTION 'TERM_CONTROL_EDIT'
              TABLES
                textlines      = lt_text
              EXCEPTIONS
                user_cancelled = 1.
          ENDIF.
      ENDCASE.
  ENDCASE.
ENDFORM. "user_command

43693814b497c14c2c1a2aba3fce6745.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值