SAP ABAP 程序中归档数据读取方式

上一篇文章记录了字段目录,归档信息结构,这篇文章记录如何通过字段目录,归档信息结构,归档对象读取归档数据。未归档数据是从数据库表直接抽取,本样例是通过归档读取方式复写sql。

发布时间:2025.05.16

示例中用两种方式实现归档数据的读取,两种方式都利用了索引读取:

  1. 用标准函数读取各个表的完整数据,下例中读取MSEG用了此种方法,这种方式可以读取归档对象中任何表的全部数据,劣势是随着读取数据量增大,耗时长。

  2. 如果索引表中有我们需要读取的全部数据,可以直接读取索引表即可,无需进一步读取表的所有数据,这样比较高效。下例中读取EKPO用了此种方法。

总结:如果需要读取的字段多,则用标准函数读取表的全部字段;如果需要读取字段少,则直接读取索引表,在建信息结构时,需要包含这些字段。

完整代码如下

REPORT zmm_rpt_538_wu5.
TABLES:mseg.
TABLES:admi_files.
TYPES: ty_t_file_and_offset TYPE STANDARD TABLE OF aind_arkey,
       BEGIN OF ty_file,
         archivekey TYPE arkey,
       END OF ty_file,
       ty_t_file TYPE STANDARD TABLE OF ty_file.

DATA: gv_current_view             TYPE tabname,   " display the result . no need in the real program
      gt_files_and_offset_from_as TYPE aind_t_arkey.  "mandatory

SELECTION-SCREEN BEGIN OF BLOCK zb WITH FRAME TITLE t1.
  SELECT-OPTIONS : s_werks FOR mseg-werks OBLIGATORY. "工厂
  SELECT-OPTIONS : s_mblnr FOR mseg-mblnr . "物料凭证
  SELECT-OPTIONS : s_lifnr FOR mseg-lifnr . "供应商
  SELECT-OPTIONS : s_matnr FOR mseg-matnr . "物料编号
SELECTION-SCREEN END OF BLOCK zb.

PARAMETERS:p_readdb TYPE boole_d DEFAULT 'X' NO-DISPLAY,
           p_readar TYPE boole_d             NO-DISPLAY,
           p_readas TYPE boole_d DEFAULT 'X' NO-DISPLAY.
SELECT-OPTIONS s_files FOR admi_files-archiv_key NO-DISPLAY.

START-OF-SELECTION.
*数据库数据获取
  SELECT m~werks,
         m~mblnr,
         m~mjahr,
         m~zeile,
         m~lifnr,
         m~matnr,
         m~menge,
         m~meins
    FROM mseg AS m
   INNER JOIN ekpo AS e ON m~ebeln = e~ebeln AND m~ebelp = e~ebelp
   WHERE m~werks IN @s_werks
     AND m~matnr IN @s_matnr
     AND m~mblnr IN @s_mblnr
     AND m~lifnr IN @s_lifnr
     AND e~pstyp = '3'
     AND e~matnr IS NOT INITIAL
    INTO TABLE @DATA(gt_data).
*归档数据获取
  PERFORM frm_integrate_data.
*&---------------------------------------------------------------------*
*& Form frm_integrate_data
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM frm_integrate_data .
  DATA:lt_selections TYPE rsds_frange_t.
  DATA:lt_selections1 TYPE rsds_frange_t.
  DATA:lt_ekpo TYPE STANDARD TABLE OF ekpo.
  DATA:lt_mseg TYPE STANDARD TABLE OF mseg.
  DATA:lr_ebeln TYPE RANGE OF ebeln.
  DATA:lr_ebelp TYPE RANGE OF ebelp.
  DATA:lv_table TYPE tabname.
  
*因为MSEG与EKPO关联,可直接过滤EBELN为空的数据
  APPEND VALUE #( sign = 'I' option = 'NE' low = '' ) TO lr_ebeln.
*筛选条件整合
  PERFORM frm_selection_build USING s_werks[]
                                    s_matnr[]
                                    s_mblnr[]
                                    s_lifnr[]
                                    lr_ebeln[]
                           CHANGING lt_selections.
*读取归档数据
  PERFORM data_read_from_archive USING lt_selections    " custom selections
                                       s_files[]        " 归档文件路径,不赋值不影响数据读取,赋值可以读取指定归档文件
                                        'X'
                                       'MSEG'           " table name
                                       'Z_MM_MATBEL04'  " 字段目录
                                       'MM_MATBEL'      " 归档对象
               CHANGING lt_mseg.   "表类型必须与抽取表明一致
  SORT lt_mseg BY mblnr zeile mjahr.


*整理已归档EBELN,EBELP用于查询EKPO
  CLEAR:lr_ebeln,lr_ebelp.
  LOOP AT lt_mseg ASSIGNING FIELD-SYMBOL(<lfs_mseg>).
    APPEND VALUE #( sign = 'I' option = 'EQ' low = <lfs_mseg>-ebeln ) TO lr_ebeln.
    APPEND VALUE #( sign = 'I' option = 'EQ' low = <lfs_mseg>-ebelp ) TO lr_ebelp.
  ENDLOOP.

  IF lr_ebeln IS NOT INITIAL.
    SORT lr_ebeln BY sign option low. DELETE ADJACENT DUPLICATES FROM lr_ebeln COMPARING ALL FIELDS.
    SORT lr_ebelp BY sign option low. DELETE ADJACENT DUPLICATES FROM lr_ebelp COMPARING ALL FIELDS.
    
*因为需要获取的字段在归档信息结构Z_DRB_EKKO_001对应的索引表中全部存在,所以只需要读取索引表的内容即可
*获取归档信息结构Z_DRB_EKKO_001对应的索引表名
    SELECT SINGLE gentab INTO @lv_table FROM aind_str2 WHERE archindex = 'Z_DRB_EKKO_001' AND active = 'X'.
    
*从索引表中抽取字段
    SELECT a~ebeln,a~ebelp
      FROM (lv_table) AS a
     WHERE ebeln IN @lr_ebeln
       AND ebelp IN @lr_ebelp
       AND pstyp = '3'
       AND matnr IS NOT INITIAL
      INTO CORRESPONDING FIELDS OF TABLE @lt_ekpo.
    SORT lt_ekpo BY ebeln ebelp.
    DELETE ADJACENT DUPLICATES FROM lt_ekpo COMPARING ebeln ebelp.
  ENDIF.

*整理归档数据整合至结果表中
  LOOP AT lt_mseg ASSIGNING <lfs_mseg>.
    READ TABLE lt_ekpo TRANSPORTING NO FIELDS WITH KEY ebeln = <lfs_mseg>-ebeln ebelp = <lfs_mseg>-ebelp BINARY SEARCH.
    IF sy-subrc = 0.
      APPEND INITIAL LINE TO gt_data ASSIGNING FIELD-SYMBOL(<lfs_data>).
      MOVE-CORRESPONDING <lfs_mseg> TO <lfs_data>.
    ENDIF.
  ENDLOOP.
*去重
  SORT gt_data BY mblnr mjahr zeile.
  DELETE ADJACENT DUPLICATES FROM gt_data COMPARING mblnr mjahr zeile.
ENDFORM.
*&---------------------------------------------------------------------*
*& frm_selection_build1
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM frm_selection_build USING VALUE(fs_werks) TYPE table
                               VALUE(fs_matnr) TYPE table
                               VALUE(fs_mblnr) TYPE table
                               VALUE(fs_lifnr) TYPE table
                               VALUE(fs_ebeln) TYPE table
                      CHANGING ft_selections   TYPE rsds_frange_t.

  FIELD-SYMBOLS: <fw_selections> LIKE LINE OF ft_selections,
                 <lw_selopt>     TYPE rsdsselopt,
                 <lw_any>        TYPE any.

  REFRESH: ft_selections.

* build table with the name of all selection options and their values
  APPEND INITIAL LINE TO ft_selections ASSIGNING <fw_selections>.
  <fw_selections>-fieldname  = 'WERKS'.
  LOOP AT fs_werks ASSIGNING <lw_any>.
    APPEND INITIAL LINE TO <fw_selections>-selopt_t
           ASSIGNING <lw_selopt>.
    MOVE-CORRESPONDING <lw_any> TO <lw_selopt>.
  ENDLOOP.

  APPEND INITIAL LINE TO ft_selections ASSIGNING <fw_selections>.
  <fw_selections>-fieldname  = 'MATNR'.
  LOOP AT fs_matnr ASSIGNING <lw_any>.
    APPEND INITIAL LINE TO <fw_selections>-selopt_t
           ASSIGNING <lw_selopt>.
    MOVE-CORRESPONDING <lw_any> TO <lw_selopt>.
  ENDLOOP.

  APPEND INITIAL LINE TO ft_selections ASSIGNING <fw_selections>.
  <fw_selections>-fieldname  = 'MBLNR'.
  LOOP AT fs_mblnr ASSIGNING <lw_any>.
    APPEND INITIAL LINE TO <fw_selections>-selopt_t
           ASSIGNING <lw_selopt>.
    MOVE-CORRESPONDING <lw_any> TO <lw_selopt>.
  ENDLOOP.

  APPEND INITIAL LINE TO ft_selections ASSIGNING <fw_selections>.
  <fw_selections>-fieldname  = 'LIFNR'.
  LOOP AT fs_lifnr ASSIGNING <lw_any>.
    APPEND INITIAL LINE TO <fw_selections>-selopt_t
           ASSIGNING <lw_selopt>.
    MOVE-CORRESPONDING <lw_any> TO <lw_selopt>.
  ENDLOOP.

  APPEND INITIAL LINE TO ft_selections ASSIGNING <fw_selections>.
  <fw_selections>-fieldname  = 'EBELN'.
  LOOP AT fs_ebeln ASSIGNING <lw_any>.
    APPEND INITIAL LINE TO <fw_selections>-selopt_t
           ASSIGNING <lw_selopt>.
    MOVE-CORRESPONDING <lw_any> TO <lw_selopt>.
  ENDLOOP.

ENDFORM.

*----------------------------------------------------------------------*
*   Read requested flight bookings from the archive.
*----------------------------------------------------------------------*
* --> FS_*                 Selection criteria
* <-- FT_TABLE             records from the archive and the database
* Global
*  <-- GT_FILES_AND_OFFSET_FROM_AS Will be needed for the access to invoices or tickets
*----------------------------------------------------------------------*
FORM data_read_from_archive USING VALUE(lt_selections)   TYPE rsds_frange_t
                                  VALUE(fs_files)    TYPE table
                                  VALUE(fp_readas)   TYPE boole_d
                                  VALUE(fv_tabname)  TYPE tabname
                                 VALUE(fs_fieldcat) TYPE aind_fcat
                                 VALUE(fs_archobj) TYPE objct_tr01

                         CHANGING ft_table           TYPE table.


  DATA: lt_obj_data                 TYPE as_t_tablebuffer,
        lt_files_read_by_object     TYPE STANDARD TABLE OF aind_arkey,
        lt_files_and_offset_from_as TYPE aind_t_arkey.
  DATA: lr_files_read_sequential    TYPE as_t_rng_archiv.
  DATA: lv_archive_name TYPE arkey,
        lv_obj_offset   TYPE admi_offst,
        lv_handle       LIKE sy-tabix.
  FIELD-SYMBOLS: <lw_file_ofs>      TYPE aind_arkey.


* prepare for reading from archive
  PERFORM archive_read_prepare USING fs_files
                                     fp_readas
                                     lt_selections
                                     fs_fieldcat
                            CHANGING lt_files_read_by_object[]
                                     lr_files_read_sequential[]
                                     lt_files_and_offset_from_as[].
  SORT lt_files_and_offset_from_as BY archivekey archiveofs.
  gt_files_and_offset_from_as[] = lt_files_and_offset_from_as[].

* read sequential
  IF fp_readas IS INITIAL OR lr_files_read_sequential[] IS NOT INITIAL.
    CALL FUNCTION 'ARCHIVE_OPEN_FOR_READ'
      EXPORTING
        object             = fs_archobj
      IMPORTING
        archive_handle     = lv_handle
      TABLES
        archive_files      = lr_files_read_sequential[]
      EXCEPTIONS
        no_files_available = 1.

    IF sy-subrc IS INITIAL.
      DO.
        CALL FUNCTION 'ARCHIVE_GET_NEXT_OBJECT'
          EXPORTING
            archive_handle = lv_handle
          IMPORTING
            object_offset  = lv_obj_offset
            archive_name   = lv_archive_name
          EXCEPTIONS
            end_of_file    = 1.
        IF NOT sy-subrc IS INITIAL.
          EXIT.
        ENDIF.

        IF NOT lt_files_and_offset_from_as[] IS INITIAL.
*         as AS was used, check if data object can include searched data
          READ TABLE lt_files_and_offset_from_as TRANSPORTING NO FIELDS
               WITH KEY archivekey = lv_archive_name
                        archiveofs = lv_obj_offset
               BINARY SEARCH.
          CHECK sy-subrc IS INITIAL.
        ENDIF.

*       read data object with I_SELECTIONS
        REFRESH lt_obj_data.
        CALL FUNCTION 'ARCHIVE_READ_OBJECT_BY_HANDLE'
          EXPORTING
            iv_handle          = lv_handle
            iv_read_class_data = 'YES'  "we don t want to read archvie clas data."If you want, do provide 'YES' or 'CONTEXT'
          CHANGING
            ct_obj_data        = lt_obj_data.

        PERFORM data_object_read USING lt_selections[]
                                       fv_tabname
                              CHANGING lt_obj_data
                                       ft_table[].
      ENDDO.

      CALL FUNCTION 'ARCHIVE_CLOSE_FILE'
        EXPORTING
          archive_handle = lv_handle.
    ELSE.
      MESSAGE ID sy-msgid TYPE 'I' NUMBER sy-msgno
         WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
  ENDIF.

* read only the relevant data objects
  SORT lt_files_read_by_object  BY archivekey archiveofs.
  LOOP AT lt_files_read_by_object ASSIGNING <lw_file_ofs>.
*   provide empty lt_obj_data only if you want to read all tables from current data object!
*   In this example we do have only one table (SFLIGHT), therefore we provide empty lt_obj_data.
*   See parameter documentation in the function module interface!
    REFRESH lt_obj_data.
    CALL FUNCTION 'ARCHIVE_READ_OBJECT_BY_OFFSET'
      EXPORTING
        iv_archivekey      = <lw_file_ofs>-archivekey
        iv_offset          = <lw_file_ofs>-archiveofs
        iv_read_class_data = 'YES'  "we don t want to read archvie clas data."If you want, do provide 'YES' or 'CONTEXT'
      CHANGING
        ct_obj_data        = lt_obj_data[].

*   read data object
    PERFORM data_object_read USING lt_selections[]
                                   fv_tabname
                          CHANGING lt_obj_data
                                   ft_table[].
  ENDLOOP.

ENDFORM.                    " data_read_from_archive
*----------------------------------------------------------------------*
*  Preparation for reading from the archive:
*  If the reading is to take place using the AS:
*  a) Certain archive files with requested data with the help of
*     function module AS_API_READ
*  b) Depending on how many data objects of an archive file are to be
*     read, decide whether it would be faster to read this file
*     sequentially or using an index.
*  Otherwise create a table with single values from the range of the
*  archive files.
*----------------------------------------------------------------------*
* --> FS_FILES                      Archive files for sequential read
*                                   accesses
* --> FT_SELECTIONS                 Selections
* <-- FT_FILES_READ_BY_OBJECT       Files+Offset for index-based read
*                                   accesses
* <-- FR_FILES_READ_READ_SEQUENTIAL Result: Files for sequential read
*                                           accesses
* <-- FT_FILES_AND_OFFSET_AS        Result: If the reading is to take
*                                   place sequentially and the files to
*                                   be read were delivered previously
*                                   by AS, you will see here later which
*                                   data objects are to be considered
*                                   during the sequential read (for
*                                   optimization purposes)
*----------------------------------------------------------------------*
FORM archive_read_prepare
         USING VALUE(fs_files)          TYPE table
               VALUE(fp_readas)         TYPE boole_d
               VALUE(ft_selections)     TYPE rsds_frange_t
               VALUE(fs_fieldcat)     TYPE aind_fcat
      CHANGING ft_files_read_by_object  TYPE ty_t_file_and_offset
               fr_files_read_sequential TYPE table
               ft_files_and_offset_as   TYPE ty_t_file_and_offset.


  DATA: lt_files_read_sequential TYPE ty_t_file.
  FIELD-SYMBOLS: <lr_file> TYPE rng_archiv,
                 <lv_file> LIKE LINE OF lt_files_read_sequential.


  REFRESH: ft_files_read_by_object, fr_files_read_sequential,
           ft_files_and_offset_as.

  IF fp_readas IS NOT INITIAL.
*   get data objects with requested data
    CALL FUNCTION 'AS_API_READ'         "1)
      EXPORTING
        i_fieldcat         = fs_fieldcat
        i_selections       = ft_selections[]
      IMPORTING
        e_result           = ft_files_and_offset_as[]
      EXCEPTIONS
        no_infostruc_found = 2.
    IF sy-subrc = 2.
      MESSAGE ID sy-msgid TYPE 'I' NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      EXIT.
    ENDIF.

*   decide about faster access kind: read sequential or
*   only relevant data objects?
    CHECK ft_files_and_offset_as[] IS NOT INITIAL.
    SORT ft_files_and_offset_as BY archivekey archiveofs.
    DELETE ADJACENT DUPLICATES FROM ft_files_and_offset_as
                               COMPARING ALL FIELDS.
    CALL FUNCTION 'ARCHIVE_FILES_ACCESS_KIND_GET'
      TABLES
        it_files_and_pos  = ft_files_and_offset_as
        et_files_seq_read = lt_files_read_sequential
        et_files_obj_read = ft_files_read_by_object.

*   convert table with archive files to a range
    LOOP AT lt_files_read_sequential ASSIGNING <lv_file>.
      APPEND INITIAL LINE TO fr_files_read_sequential
             ASSIGNING <lr_file>.
      <lr_file>-sign   = 'I'.
      <lr_file>-option = 'EQ'.
      <lr_file>-low    = <lv_file>.
    ENDLOOP.

  ELSEIF fs_files[] IS NOT INITIAL.
    fr_files_read_sequential[] = fs_files[].
  ENDIF.

ENDFORM.                    " archive_read_prepare

*----------------------------------------------------------------------*
* Read data of the current data object:
* The check against the user#s selections is necessary if the
* possibility exists that a data object contains data that was not
* selected, or if not all fields for which the user made a selection are
* contained in the used infostructure.
*----------------------------------------------------------------------*
*  -->  FT_SELECTIONS          User#s selection.
*  <--> FT_OBJ_DATA            Pointer to the data of the current data object
*  <--  FT_TABLE       Result: records from the archive
*----------------------------------------------------------------------*
FORM data_object_read USING VALUE(ft_selections) TYPE rsds_frange_t
                            VALUE(fv_tabname)    TYPE tabname
                   CHANGING ft_obj_data          TYPE as_t_tablebuffer
                            ft_table             TYPE table.

  DATA: lv_indx  LIKE sy-tabix.
  FIELD-SYMBOLS: <lw_table>       TYPE any,
                 <lw_selections>  LIKE LINE OF ft_selections,
                 <lw_obj_data>    LIKE LINE OF ft_obj_data,
                 <lt_table>       TYPE STANDARD TABLE,
                 <lv_field_value> TYPE any.

  READ TABLE ft_obj_data ASSIGNING <lw_obj_data> WITH KEY tabname = fv_tabname.

  CHECK sy-subrc IS INITIAL.
  ASSIGN <lw_obj_data>-tabref->* TO <lt_table>.
  LOOP AT <lt_table> ASSIGNING <lw_table>.
    lv_indx = sy-tabix.
    LOOP AT ft_selections ASSIGNING <lw_selections>.
      ASSIGN COMPONENT <lw_selections>-fieldname
          OF STRUCTURE <lw_table> TO <lv_field_value>.
      CHECK sy-subrc IS INITIAL.
      IF NOT <lv_field_value> IN <lw_selections>-selopt_t.
        DELETE <lt_table> INDEX lv_indx.
        EXIT.
      ENDIF.
    ENDLOOP.
  ENDLOOP.

  APPEND LINES OF <lt_table> TO ft_table.

ENDFORM.                    " data_object_read
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值