2018年11月1日 星期四

lll

ABAP程序性能优化的一些做法(应该用那些优秀的写法)

ABAP程序基本上都需要从数据库里面抓数,所以性能很重要,
同时有一些基本的,和优秀的写法是我们必须要掌握的,
不然就会造成程序性能很差。
下面给予总结(这里包括有很基本的,也包括有比较少用到的),
顺便推荐一个好的SAP标准文档
ABAP_PERFORMANCE_DOS_AND_DONTS  :

一、基本的几条需要避免的规则(具体的一些怎么替换,可以看三和五):
        1、不使用 select....endselect,估计月球人都知道这个事实了。
        2、基本不使用 select *  ,跟多人喜欢直接用这个,因为方便,但是在数据量比较大的时候,应该使用select 字段,这样可以避免抓取无用数据。
        3、LOOP 里面不用 sort  ,在 loop 外面排序完再进入 LOOP。
        4、尽量避免 LOOP  里面嵌套 select,这样多次访问数据库也会造成性能问题,但是有些时候避免不了也难免。改为外面 select,LOOP 里面 read table。
        5、大数据的 read table,使用二分法  BINARY SEARCH,用之前要按关键字排序。
        6、尽量避免 LOOP 里面嵌套 LOOP,特别是当两个内表数据量都很大的时候,如果实在要嵌套 LOOP 可以参考三和五里面的解决办法。
        7、尽量避免 LOOP 里面不用 delete,append 等语句。改成批量处理。
=========================================================
二、index和buffer的合理使用:
        1、index 的使用,在使用现有的 index 的时候注意,where 条件里面的字段的顺序要跟index 一致,而且可以可以适当的去匹配 index,创建一些空的字段或者是 index 后面再加字段,或者是使用 index 抓出数据后,再去做其它条件的处理。
        2、index 里面最好只有 '='  AND 或者是‘IN’。有其它逻辑条件,会影响index的使用。
        3、 如果表是有buffer,可以考虑使用buffer,这样性能也可以有很好的提升,不过首先要去表看看这个是否有buffer:
                SELECT  SINGLE *  FROM   T100  INTO T100_WA
                BYPASSING BUFFER 
                WHERE   SPRSL = 'D'.
         4、大部分表都有buffer,而且这个我们很难控制,但是我们写出来的语句要经可能的避免“不读buffer”:
         5、一般程序到表抓数的时候,会在应用服务层有数据缓存,所以同一个程序,在不同时间先后跑,后面的通常会比较快,因为可以到数据缓存读书。
         6、NOT,只能全表扫描,不要用not,换成反面。建议between 换成IN。不建议使用LIKE OR
         7、


三、使用一些简单的语句代替复杂的嵌套LOOP:
        1、用批量处理的BAPI替换,LOOP  里面call BAPI,例如用BAPI_MATERIAL_SAVEREPLICA   替代  BAPI_MATERIAL_SAVEDATA
        2、LOOP 里面delete,可以改成,给内表加一个flag的字段,然后需要删除的打上X,然后用Delete it_1 where delete = ‘X’.
        3、LOOP AT 搭INSERT或者是APPEND,可以改成:   INSERT   SBOOK     FROM   TABLE   itab。  APPEND LINES 
        4、如果是两个内表都很多数据,但是逻辑要进行嵌套LOOP,可以如下处理,会提升一些性能:
             方法一:
              sort itab2 by aa.   这个地方看上去像是两个LOOP,其实是只有一个。
               loop itab1
                      read itab2 aa = itab-11 binary search.
                      loop itab2 from sy-taibx.
                     endloop.
              endloop.
              方法二:
              
              方法三:
              

          5、LOOP 后面用assigning   <fs>指针的方式,这样也可以节省空间和时间:可以省去了append,modify等操作,在嵌套LOOP没法像上面那样解决的话,也建议使用指针。
                 FIELD-SYMBOLS :       <FS>   TYPE   VBAK.
                 LOOP AT   gt_vbak    ASSIGNING   <FS>   Where  lifsk  = '01'.
                 <fs>-space = '02'.
            6、


四、有一些标准的FM,如果在LOOP里面使用可以考虑换成使用自己开发,把抓数放出来,然后read(read_text):
           1、有很多程序要调用FM read_text,但是当用到LOOP里面调用这个read_text,会比较慢其中到STXH表抓数就会占用很多时间,我们可以考虑对read_text进行优化,把read_text分成两个FM,一个是集中读取STXH,然后另一个是和read_text一样的功能,只是把抓数换成read table。占用性能就会提高很多。
            2、



五、一些优秀的良好的ABAP 程序书写习惯:
           1、select  数据的时候,如果可以使用join进行内联,尽量2-3个表内联即可。下面这个图可以理解下join的用法:
                  有时,join太多了,也可以考虑使用创建视图,然后从视图里抓数,例如vbak_kan1。
              
           2、如果要select 数据出来,更改一些字段的值,再进行updata,可以考虑直接使用updata   set,省去到表里面抓数:
                  SELECT * FROM sbook
                  INTO xbook
                  WHERE carrid = 'LH' AND connid = '0400' AND fldate >= '20110101'.
                  xbook-connid = '0500'.
                  UPDATE sbook FROM xbook.
                  ENDSELECT.

                 UPDATE sbook
                 SET connid = '0500' WHERE carrid = 'LH' AND connid = '0400' AND fldate >= '20110101'.
           3、读取HASHED表会比其他两种类型更快,同时read table 也可以通过transporting  某些字段到work area,不用全部字段都用上。
           4、有些表的数据,是很固定的,例如KNA1或者是T001等,可以抓大部分数据放到内表里,然后去读取,如果读取不到,在抓,不用每次都抓。
           5、强制使用索引:(但是如果更改数据库了,就会失效了)      %_HINTS DB6            (这个不太建议使用,但是当ST05分析之后,是可以使用某个索引,但系统没有使用,就可以使用这个语句强制使用索引。
           6、当一个report在ALV显示完之后,在end-of-selection里面把不用的内表的清空掉,防止有些后台job一次跑两个变量的时候会重复,同时释放内表也是减少内存压力。
           7、field-groups的使用,对于多层次的排序和显示非常有用。
















ABAP多线程程序运行              
Author: Chaim Wang

Date: 2018/7/19

 

在ABAP中,有时因为数据量过大可能会影响用户的体验度,导出一点数据有时都会等上半个小时,为了避免这种情况,在此引用大数据的基本原理并行执行数据。减少系统运行的时间,就必须要牺牲内存或者CPU的,这里只仅仅是说明如何牺牲CPU提高程序运行速度在ABAP的执行方式。



如上图所示若将一个程序的作业量平均分成两份,则运行的时间将变成原来的一半。以此类推在扩展,数据查询时间将以成倍减少。

SUBMIT的运用
Submit在ABAP中调用其他程序的方式,(比如我们现在正在运行A程序,但是中间需要B程序运行并给我们返回数据,一般的方式我们都会直接复制B程序到A程序然后在执行)。其实submit可以很容易的传入输入参数返回输出参数在内存。关于使用方法可以参考网络或一下代码

FIELD-SYMBOLS: <Itab> TYPE data.
FIELD-SYMBOLS:<lt_data>  TYPE ANY TABLE,
              <lt_data_line>  TYPE ANY TABLE.
DATA:         lr_data               TYPE REF TO data,
              lr_data_line TYPE REF TO data.
DATA:         lr_data_descr          TYPE REF TO cl_abap_datadescr,
              lr_data_descr_line TYPE REF TO cl_abap_datadescr.
DATA:FLAG TYPE C VALUE ''.

DATA: lt_selscreen LIKE rsparams OCCURS 0.
DATA: wa_selscreen LIKE LINE OF lt_selscreen.

CLEAR:flag. "flag初始化
         cl_salv_bs_runtime_info=>set(
          EXPORTING display  = abap_false
                        metadata = abap_false
                        data     = abap_true ).
*         Submit standard program with selection table
       SUBMIT ZPPR0008 WITH SELECTION-TABLE lt_selscreen "
                          WITH p_werks = WERK_TAB-WERKSA
          AND RETURN.
        TRY."因MB51中ALV输出有header,list 所以要有2个参数
            cl_salv_bs_runtime_info=>get_data_ref(
                 IMPORTING r_data_descr      = lr_data_descr ).
                        "   r_data_line_descr = lr_data_descr_line
            IF lr_data_descr IS NOT INITIAL .
              CREATE DATA lr_data TYPE HANDLE lr_data_descr.
         "   CREATE DATA lr_data_line TYPE HANDLE lr_data_descr_line.
            ASSIGN lr_data->* TO <lt_data>.
         "   ASSIGN lr_data_line->* TO <lt_data_line>.
            cl_salv_bs_runtime_info=>get_data(
              IMPORTING
                t_data      =      <lt_data>
           "     t_data_line      =      <lt_data_line>
                   ).
            ELSE.
             "  MESSAGE '讯息:没有符合您设定的范围数据!!' TYPE 'E'.
                flag = 'X'.
            ENDIF.
          CATCH cx_salv_bs_sc_runtime_info.
          "  MESSAGE `Unable to retrieve ALV data` TYPE 'E'.
            flag = 'X'.
        ENDTRY.
        cl_salv_bs_runtime_info=>clear_all( ).
      IF flag = ''.
        LOOP AT <lt_data> ASSIGNING <Itab>.
              MOVE-CORRESPONDING <Itab> to IT_ITAB.
              APPEND IT_ITAB.
              CLEAR IT_ITAB.
        ENDLOOP.
      ENDIF.

FORM SHUJU TABLES Tab  USING A.
FIELD-SYMBOLS: <Itab1> TYPE data.
LOOP AT Tab ASSIGNING <Itab1>.
  MOVE-CORRESPONDING <Itab1> to wa_selscreen.
  wa_selscreen-selname = A.
  wa_selscreen-kind = 'S'.
APPEND wa_selscreen TO lt_selscreen.
CLEAR: wa_selscreen.
ENDLOOP.
ENDFORM.

以上是SUBMIT的处理方法,但是不足的是这个也是串行运行程序,程序运行在此处时会等待返回结果后才继续运行下去。

后台运行
其实submit可以使用后台运行,但是数据不能通过内存传输过来,运行完毕后就丢失了,所以这是我们要关注的解决的主要问题点,如果我们想想在ABAP中启动这个后台运行程序,将结果的数据在传入我们此时正在运行的程序,那么程序可以按倍数的降低运行时间。

DATA: JOBCOUNT LIKE TBTCJOB-JOBCOUNT,
      HOST LIKE MSXXLIST-HOST.
DATA: BEGIN OF STARTTIME.
        INCLUDE STRUCTURE TBTCSTRT.
DATA: END OF STARTTIME.
DATA: STARTTIMEIMMEDIATE LIKE BTCH0000-CHAR1 VALUE 'X'.
DATA: actual_status LIKE TBTCJOB-STATUS .
DATA e_return TYPE INT4.
DATA e_message TYPE CHAR40.
DATA joblogtbl LIKE RANGE OF TBTC5 WITH HEADER LINE.
DATA: JOBNAME LIKE TBTCJOB-JOBNAME.

“开始启动后台运行的方式。

CONCATENATE 'ZM08' SY-DATUM SY-UZEIT SY-UNAME INTO JOBNAME.
PERFORM JOB_OPEN CHANGING JOBNAME JOBCOUNT.
SUBMIT Z_YOUHUA_ZM08_2  WITH SELECTION-TABLE lt_selscreen
                        WITH JOBNAME  =  JOBNAME
AND RETURN
                        USER SY-UNAME
                        VIA JOB JOBNAME
                        NUMBER JOBCOUNT.
IF SY-SUBRC > 0.
     EXIT.
ENDIF.
PERFORM JOB_CLOSE USING JOBNAME JOBCOUNT.

放入A程序运行的程序

A程序运行结束后或者需要B程序的返回数据时

PERFORM JOB_CHECKSTATE USING jobname jobcount.

 

FORM JOB_OPEN CHANGING JOBNAME JOBCOUNT.
  CALL FUNCTION 'JOB_OPEN'
  EXPORTING
    DELANFREP        = ' '
    JOBGROUP         = ' '
    JOBNAME          = JOBNAME
    SDLSTRTDT        = SY-DATUM
    SDLSTRTTM        = SY-UZEIT
  IMPORTING
    JOBCOUNT         = JOBCOUNT
  EXCEPTIONS
    CANT_CREATE_JOB  = 01
    INVALID_JOB_DATA = 02
    JOBNAME_MISSING  = 03.

IF SY-SUBRC NE 0.
   EXIT.
ENDIF.
ENDFORM.

FORM JOB_CLOSE USING JOBNAME JOBCOUNT.
CALL FUNCTION 'JOB_CLOSE'
  EXPORTING
    EVENT_ID             = STARTTIME-EVENTID
    EVENT_PARAM          = STARTTIME-EVENTPARM
    EVENT_PERIODIC       = STARTTIME-PERIODIC
    JOBCOUNT             = JOBCOUNT
    JOBNAME              = JOBNAME
*    LASTSTRTDT           = STARTTIME-LASTSTRTDT
*    LASTSTRTTM           = STARTTIME-LASTSTRTTM
    PRDDAYS              = 0                    "周期执行
    PRDHOURS             = 0
    PRDMINS              = 0
    PRDMONTHS            = 0
    PRDWEEKS             = 0
*    SDLSTRTDT            = STARTTIME-SDLSTRTDT
*    SDLSTRTTM            = STARTTIME-SDLSTRTTM  "执行最后期限
    STRTIMMED            = STARTTIMEIMMEDIATE    "立刻执行
    TARGETSYSTEM         = HOST
  EXCEPTIONS
    CANT_START_IMMEDIATE = 01
    INVALID_STARTDATE    = 02
    JOBNAME_MISSING      = 03
    JOB_CLOSE_FAILED     = 04
    JOB_NOSTEPS          = 05
    JOB_NOTEX            = 06
    LOCK_FAILED          = 07
    OTHERS               = 99.

IF SY-SUBRC EQ 0.
  "error processing
ENDIF.
ENDFORM.

检测后台运行状态。
FORM JOB_CHECKSTATE USING jobname jobcount.
           CALL FUNCTION 'BP_JOB_CHECKSTATE'
            EXPORTING
              dialog                       = 'N'
              jobcount                     = jobcount
              jobname                      = jobname
            IMPORTING
              actual_status                = actual_status
            EXCEPTIONS
              checking_of_job_has_failed   = 1
              correcting_job_status_failed = 2
              invalid_dialog_type          = 3
              job_does_not_exist           = 4
              no_check_privilege_given     = 5
              ready_switch_too_dangerous   = 6
              OTHERS                       = 7.
          IF sy-subrc <> 0.
            e_return = 8.  "JOBSTATUS取得失敗
            e_message = text-008.
            RETURN.
          ENDIF.
          IF ( actual_status = 'F' )
            OR ( actual_status = 'A' ).
            RETURN.
          ENDIF.
     DO 7200 TIMES.
          WAIT UP TO 2 SECONDS.
          CALL FUNCTION 'BP_JOB_CHECKSTATE'
            EXPORTING
              dialog                       = 'N'
              jobcount                     = jobcount
              jobname                      = jobname
            IMPORTING
              actual_status                = actual_status
            EXCEPTIONS
              checking_of_job_has_failed   = 1
              correcting_job_status_failed = 2
              invalid_dialog_type          = 3
              job_does_not_exist           = 4
              no_check_privilege_given     = 5
              ready_switch_too_dangerous   = 6
              OTHERS                       = 7.
          IF sy-subrc <> 0.
            e_return = 8.  "JOBSTATUS取得失敗
            e_message = text-008.
            EXIT.
          ENDIF.
          IF ( actual_status = 'F' )
            OR ( actual_status = 'A' ).
            EXIT.
          ENDIF.
    ENDDO.
 ENDFORM.

 

3、为了避免相同程序重复运行产生的后台任务冲突,我们可以将作业名取名为用户加当前日期时间,用户不可能在同一时间开启两个相同任务运行。就算两个窗口,点击执行也有时间差,所以此问题就可以很好的避免了。

4、程序的后台运行我们已经启动了,现在关键是如何获取B程序运行的数据。

其实这里是将B运行的数据存入数据表里,再在A程序的数据表里数据查找出来,并删除数据表。建立数据表要注意一下条件。

(1)、防止多个程序的运行,数据表传入数据,或写入数据的混乱。

(2)、当有意外情况数据表里的数据未能删除,列入程序运行一般退出了。

(3)、当一个程序有多个数据表传输数据,可以用全用一个表来满足数据的传输。

所以首先我们要以作业名字为关键字。这样可以解决多个应用程序运行互不干扰,在以一个程序的标识作为关键字以防止多个表的数据传输在一个表里互不干扰,最后在一个序号作为关键字,以表示每个表数据记录的真实性。为了防止第二中情况的发生可以在数据表里加入日期,当数据表在新的一天运行的时候发现数据表里有数据,可以先进行删除不是这一天和上一天的数据。此可避免异常产生的数据的永久存放数据表。



图3 关键字数据表
--------------------- 
作者:WangChaim 
来源:CSDN 
原文:https://blog.csdn.net/Charmean/article/details/81114072 
版权声明:本文为博主原创文章,转载请附上博文链接!