上一篇文章记录了字段目录,归档信息结构,这篇文章记录如何通过字段目录,归档信息结构,归档对象读取归档数据。未归档数据是从数据库表直接抽取,本样例是通过归档读取方式复写sql。
发布时间:2025.05.16
示例中用两种方式实现归档数据的读取,两种方式都利用了索引读取:
-
用标准函数读取各个表的完整数据,下例中读取MSEG用了此种方法,这种方式可以读取归档对象中任何表的全部数据,劣势是随着读取数据量增大,耗时长。
-
如果索引表中有我们需要读取的全部数据,可以直接读取索引表即可,无需进一步读取表的所有数据,这样比较高效。下例中读取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