SAP RFC函数RFC_READ_TABLE使用与优化2

原来的优化方案

问题:

  1. 对于字段总长超过512X5的表仍然读不了
  2. 数据量大时性能很差(分页查询效率很低)

解决方案

  1. 将每行记录的数据拆分为多行直接塞入到DATA表而不分到DATA1~DATA4中
    并将每行记录占用的行数通过字段返回
    客户端根据每行记录占用的data行数重新拼接data使用。
  2. RFC_READ_TABLE源码中,在SELECT. ENDSELECT.语句中拼接处理字段,遇到大表效率非常低
SELECT * FROM (QUERY_TABLE) INTO <WA> WHERE (OPTIONS).

    IF SY-DBCNT GT ROWSKIPS.

*   copy all relevant fields into DATA (output) table
      LOOP AT FIELDS_INT.
        IF FIELDS_INT-TYPE = 'P'.
        ASSIGN COMPONENT FIELDS_INT-FIELDNAME
            OF STRUCTURE <WA> TO <COMP>
            TYPE     FIELDS_INT-TYPE
            DECIMALS FIELDS_INT-DECIMALS.
        ELSE.
        ASSIGN COMPONENT FIELDS_INT-FIELDNAME
            OF STRUCTURE <WA> TO <COMP>
            TYPE     FIELDS_INT-TYPE.
        ENDIF.
        MOVE <COMP> TO
            <D>+FIELDS_INT-OFFSET_DST(FIELDS_INT-LENGTH_DST).
      ENDLOOP.
*   end of loop at FIELDS_INT
      APPEND DATA.

      IF ROWCOUNT > 0 AND SY-DBCNT GE ROWCOUNT. EXIT. ENDIF.

    ENDIF.

  ENDSELECT.
  • SAP 751后sql查询语法增加了offset关键字可以实现分页,参考官方文档
    可以先分页查询出结果,再循环内表拼接字段,大幅提高效率。
  • SAP 751之前版本没有offset关键字,则采用如下折中方法
  1. ROWSKIPS=0的请求,直接通过UP TO关键字控制查询结果行数,一次性查出记录
  2. ROWSKIPS>0的请求,通过SELECT. ENDSELECT.实现分页查询。
  3. 将1,2两步查询结果,通过循环内表处理拼接DATA。

优化后的源码全文

function zrfc_read_table .
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     VALUE(QUERY_TABLE) LIKE  DD02L-TABNAME
*"     VALUE(DELIMITER) LIKE  SONV-FLAG DEFAULT SPACE
*"     VALUE(NO_DATA) LIKE  SONV-FLAG DEFAULT SPACE
*"     VALUE(ROWSKIPS) LIKE  SOID-ACCNT DEFAULT 0
*"     VALUE(ROWCOUNT) LIKE  SOID-ACCNT DEFAULT 0
*"  EXPORTING
*"     VALUE(LINES_PER_RECORD) TYPE  INTEGER
*"  TABLES
*"      OPTIONS STRUCTURE  RFC_DB_OPT
*"      FIELDS STRUCTURE  RFC_DB_FLD
*"      DATA STRUCTURE  TAB512
*"  EXCEPTIONS
*"      TABLE_NOT_AVAILABLE
*"      TABLE_WITHOUT_DATA
*"      OPTION_NOT_VALID
*"      FIELD_NOT_VALID
*"      NOT_AUTHORIZED
*"      DATA_BUFFER_EXCEEDED
*"----------------------------------------------------------------------
  "
  call function 'VIEW_AUTHORITY_CHECK'
    exporting
      view_action                    = 'S'
      view_name                      = query_table
    exceptions
      no_authority                   = 2
      no_clientindependent_authority = 2
      no_linedependent_authority     = 2
      others                         = 1.

  if sy-subrc = 2.
    raise not_authorized.
  elseif sy-subrc = 1.
    raise table_not_available.
  endif.

* ----------------------------------------------------------------------
*  find out about the structure of QUERY_TABLE
* ----------------------------------------------------------------------
  data begin of table_structure occurs 10.
  include structure dfies.
  data end of table_structure.
  "DATA TABLE_HEADER LIKE X030L.
  data table_type type dd02v-tabclass.

  call function 'DDIF_FIELDINFO_GET'
    exporting
      tabname        = query_table
*     FIELDNAME      = ' '
*     LANGU          = SY-LANGU
*     LFIELDNAME     = ' '
*     ALL_TYPES      = ' '
*     GROUP_NAMES    = ' '
    importing
*     X030L_WA       =
      ddobjtype      = table_type
*     DFIES_WA       =
*     LINES_DESCR    =
    tables
      dfies_tab      = table_structure
*     FIXED_VALUES   =
    exceptions
      not_found      = 1
      internal_error = 2
      others         = 3.
  if sy-subrc <> 0.
    raise table_not_available.
  endif.
  if table_type = 'INTTAB'.
    raise table_without_data.
  endif.

* ----------------------------------------------------------------------
*  isolate first field of DATA as output field
*  (i.e. allow for changes to structure DATA!)
* ----------------------------------------------------------------------
  data line_length type i.
  field-symbols <d>.
  assign component 0 of structure data to <d>.
  describe field <d> length line_length in character mode.
* ----------------------------------------------------------------------
*  if FIELDS are not specified, read all available fields
* ----------------------------------------------------------------------
  data number_of_fields type i.
  describe table fields lines number_of_fields.
  if number_of_fields = 0.
    loop at table_structure.
      move table_structure-fieldname to fields-fieldname.
      append fields.
    endloop.
  endif.

* ----------------------------------------------------------------------
*  for each field which has to be read, copy structure information
*  into tables FIELDS_INT (internal use) and FIELDS (output)
* ----------------------------------------------------------------------
  data: begin of fields_int occurs 10,
          fieldname  like table_structure-fieldname,
          type       like table_structure-inttype,
          decimals   like table_structure-decimals,
          length_src like table_structure-intlen,
          length_dst like table_structure-leng,
          offset_src like table_structure-offset,
          offset_dst like table_structure-offset,
        end of fields_int,
        line_cursor type i.
* 行数据缓存,此处按字段位置初始化分隔符
  data record_buffer(30000).
  line_cursor = 0.
*  for each field which has to be read ...
  loop at fields.

    read table table_structure with key fieldname = fields-fieldname.
    if sy-subrc ne 0.
      raise field_not_valid.
    endif.

* compute the place for field contents in DATA rows:
* if not first field in row, allow space for delimiter
    if line_cursor <> 0.
      if no_data eq space and delimiter ne space.
        move delimiter to record_buffer+line_cursor.
      endif.
      line_cursor = line_cursor + strlen( delimiter ).
    endif.

* ... copy structure information into tables FIELDS_INT
* (which is used internally during SELECT) ...
    fields_int-fieldname  = table_structure-fieldname.
    fields_int-length_src = table_structure-intlen.
    fields_int-length_dst = table_structure-leng.
    fields_int-offset_src = table_structure-offset.
    fields_int-offset_dst = line_cursor.
    fields_int-type       = table_structure-inttype.
    fields_int-decimals   = table_structure-decimals.
* compute the place for contents of next field in DATA rows
    line_cursor = line_cursor + table_structure-leng.
* 超长记录拆分行,不再报缓冲溢出的异常
*    if line_cursor > line_length and no_data eq space.
*      raise data_buffer_exceeded.
*    endif.

    append fields_int.

* ... and into table FIELDS (which is output to the caller)
    fields-fieldtext = table_structure-fieldtext.
    fields-type      = table_structure-inttype.
    fields-length    = fields_int-length_dst.
    fields-offset    = fields_int-offset_dst.
    modify fields.

  endloop.
* end of loop at FIELDS
* 每条记录需要数据行数
  data: lc type f,
        ll type f.
  lc = line_cursor.
  ll = line_length.
  lines_per_record = ceil( lc / ll ).
* ----------------------------------------------------------------------
*  read data from the database and copy relevant portions into DATA
* ----------------------------------------------------------------------
* output data only if NO_DATA equals space (otherwise the structure
* information in FIELDS is the only result of the module)
  if no_data eq space.
* PSR-----------
* 定义表ITAB结构
    field-symbols <itab> type standard table.
    data(comp_tab) = cast cl_abap_structdescr(
      cl_abap_typedescr=>describe_by_name(
        query_table
      )
    )->get_components( ).
    data(struct_descr) = cl_abap_structdescr=>create( comp_tab ).
    data(table_descr) = cl_abap_tabledescr=>create( struct_descr ).
    data table_ref type ref to data.
    create data table_ref type handle table_descr.
    assign table_ref->* to <itab>.
* 定义行记录W_ITAB结构
    data: begin of work,
            buffer(30000),
          end of work.
    field-symbols: <w_itab> type any, <comp> type any.
    assign work to <w_itab> casting type (query_table).

* 准备select
    data: select  type table of edpline,
          sublist type edpline.
    loop at fields_int.
      sublist = fields_int-fieldname && ','.
      append sublist to select.
    endloop.
    assign select[ lines( select ) ] to field-symbol(<lastselect>).
    replace `,` in <lastselect>  with ``.

* 查询
    if rowskips > 0.
      if rowcount > 0.
        rowcount = rowcount + rowskips.
      endif.
      select (select)
        from (query_table)
        into @<w_itab>
        where (options)
        order by primary key.
        if sy-dbcnt gt rowskips.
          append <w_itab> to <itab>.
          if rowcount > 0 and sy-dbcnt ge rowcount. exit. endif.
        endif.
      endselect.
    else.
      select (select)
        from (query_table)
        into table @<itab>
*          OFFSET @rowskips
        up to @rowcount rows
        where (options)
        order by primary key.
    endif.
*   处理返回数据
    data data_offset type i.
    loop at <itab> into <w_itab>.
      loop at fields_int.
        if fields_int-type = 'P'.
          assign component fields_int-fieldname
              of structure <w_itab> to <comp>
              type     fields_int-type
              decimals fields_int-decimals.
        else.
          assign component fields_int-fieldname
              of structure <w_itab> to <comp>
              type     fields_int-type.
        endif.
        move <comp> to record_buffer+fields_int-offset_dst(fields_int-length_dst).
      endloop.
      data_offset = 0.
      do lines_per_record times.
        move record_buffer+data_offset(line_length) to <d>.
        append data.
        data_offset = data_offset + line_length.
      enddo.
    endloop.
  endif.
endfunction.

posted on 2022-04-11 22:38  路过君  阅读(236)  评论(0编辑  收藏  举报

导航