SAP ABAP后台导出大批量数据及本地导出数据程序

2023-10-13 18:37:26 浏览数 (1)

为了应对审计导出大量凭证的要求,写了此程序来导出bkpf及bseg表数据,此程序分两种导出方式,导出至本地及导出至服务器,因为数据量巨大,所以也支持后台导出至服务器(导出至本地不支持后台执行,sap服务器没办法把文件写到本地)。

经过测试导出到服务器的速度目前是,100万张凭证生成一个100兆的文件导出时间1分钟左右,但是这种方法有个弊端就是无法控制单元格格式(如果有大佬知道,欢迎留言)只能控制单元格写入的内容

程序设计的思路:

1.创建个自建表,维护两个表中需要导出的字段清单

2.取自建表清单数据,构造动态内表

3.构造动态取表字段数据

4.将动态数据存储至动态内表

5.根据自建表取表字段的描述,构造导出结构的表头

6.循环取到数据的动态内表,构造导出文件

7.导出至本地或者服务器

具体代码如下

代码语言:javascript复制
REPORT ZFIR252.

TABLES :BKPF,BSEG.
TABLES:SSCRFIELDS.
DATA:OK_CODE LIKE SY-UCOMM.
DATA:STBL TYPE LVC_S_STBL.
DATA: GT_FIELD  TYPE LVC_T_FCAT.
DATA: GT_FIELD2 TYPE LVC_T_FCAT.
DATA: GS_FIELD  TYPE LVC_S_FCAT.
FIELD-SYMBOLS:<ITAB> TYPE STANDARD TABLE,
              <WA>,<FT> TYPE LVC_T_STYL,
              <FV>,<F1>,<F2>,<F3>.
FIELD-SYMBOLS:<ITAB2> TYPE STANDARD TABLE,
              <WA2>,<FT2> TYPE LVC_T_STYL,
              <FV2>,<F12>,<F22>,<F32>.
DATA: DY_TABLE TYPE REF TO DATA,   ""取值的动态内表
      DY_LINE  TYPE REF TO DATA.
DATA: DY_TABLE2 TYPE REF TO DATA,  ""输出的动态内表,每个字段暂定char128,
      DY_LINE2  TYPE REF TO DATA.
FIELD-SYMBOLS: <DYN_TABLE> TYPE TABLE,
               <GT_TABLE>  TYPE TABLE,
               <DYN_WA>    TYPE ANY.
FIELD-SYMBOLS : <FS_TAB> TYPE ANY TABLE .

SELECTION-SCREEN BEGIN OF BLOCK A1 WITH FRAME TITLE TEXT-001.
SELECT-OPTIONS:S_BUKRS FOR BKPF-BUKRS   . "公司代码
SELECT-OPTIONS:S_BLART FOR BKPF-BLART   . "凭证类型
SELECT-OPTIONS:S_BELNR FOR BKPF-BELNR   . "会计凭证
SELECT-OPTIONS:S_HKONT FOR BSEG-HKONT   . "会计科目
SELECT-OPTIONS:S_BUDAT FOR BKPF-BUDAT OBLIGATORY  . "过账日期
PARAMETERS : P_FILE LIKE RLGRAP-FILENAME DEFAULT '/usr/sap/tmp/'.   "/gacmotor/sap "AL11中的文件目录都可以导出,需要与basis联系询问具体地址
PARAMETERS : P_NUMB TYPE SY-INDEX DEFAULT '1000000' .
PARAMETERS : P_SERV RADIOBUTTON GROUP R1 DEFAULT 'X'.
PARAMETERS : P_LOCA RADIOBUTTON GROUP R1 .
SELECTION-SCREEN END OF BLOCK A1.
SELECTION-SCREEN FUNCTION KEY 1. "

AT SELECTION-SCREEN ON VALUE-REQUEST FOR P_FILE.
  PERFORM FRM_F4_P_FILE .
*************************************************************************
** INITIALIZATION
*************************************************************************
  "该事件在屏幕未显示之前执行,对程序设置值及屏幕元素进行初始化赋值。

INITIALIZATION.
  STBL-ROW = 'X'.
  STBL-COL = 'X'.
  SSCRFIELDS-FUNCTXT_01 = '维护审计导出字段配置表'.
  SSCRFIELDS-UCOMM      = 'FC01'.
*************************************************************************
** AT SELECTION SCREEN
*************************************************************************
AT SELECTION-SCREEN.
  CASE SSCRFIELDS-UCOMM.          "处理按钮命令
    WHEN'FC01'.

      CALL FUNCTION 'VIEW_MAINTENANCE_CALL'
        EXPORTING
          ACTION    = 'U'
          VIEW_NAME = 'ZTFI252'.
    WHEN OTHERS.

  ENDCASE.


*************************************************************************
** AT SELECTION SCREEN OUTPUT
*************************************************************************
AT SELECTION-SCREEN OUTPUT.

*************************************************************************
** EVENT TOP OF PAGE
*************************************************************************
TOP-OF-PAGE.

*************************************************************************
* EVENT START OF SELECTION
*************************************************************************
START-OF-SELECTION.

  PERFORM GET_DATA .


*************************************************************************
**EVENT END-OF SELECTION
*************************************************************************
END-OF-SELECTION.

*************************************************************************
**EVENT  END-OF PAGE
*************************************************************************
END-OF-PAGE.

FORM GET_DATA .

  DATA LV_SELET TYPE STRING . ""select 哪些字段
  DATA LV_SELEB TYPE STRING . ""select 哪些字段
  DATA LV_SELEA TYPE STRING . ""select 哪些字段
  DATA LV_LENGE TYPE I .
  DATA LV_STR   TYPE STRING . "用于字段转换位string类型
  DATA LT_BKPF TYPE TABLE OF BKPF .
  DATA LT_BSEG TYPE TABLE OF BSEG .
  DATA LS_BKPF TYPE BKPF .
  DATA LS_BSEG TYPE BSEG .
  DATA LINE       TYPE STRING.""行数据
  DATA LINE1      TYPE STRING.""第一行表头
  DATA LINES      TYPE STRING.""表数据
  DATA LD_XSTRING TYPE XSTRING.
  DATA LD_STRING  TYPE STRING.
  DATA WF_MSG     TYPE STRING.
  TYPES: BEGIN OF ty_fieldname,
           name TYPE char20,
         END OF ty_fieldname.
  DATA: lt_fieldname TYPE TABLE OF ty_fieldname WITH HEADER LINE.
  FIELD-SYMBOLS: <lt_table> TYPE table.

  "取配置表数据
  SELECT * FROM ZTFI252 INTO TABLE @DATA(LT_252) .
  IF LT_252 IS INITIAL.
    MESSAGE '请先维护表ZTFI252' TYPE 'S' DISPLAY LIKE 'E' .
  ENDIF .
  CHECK LT_252 IS NOT INITIAL .
  "排序,删除重复维护的字段,活动取值不能有相同的字段
  SORT LT_252 BY REF_FIELD REF_TABLE  XH .
  DELETE ADJACENT DUPLICATES FROM LT_252 COMPARING  REF_FIELD .
  SORT LT_252 BY XH REF_TABLE REF_FIELD    . "删除后按照顺序拍好
  CLEAR : GT_FIELD .
  LOOP AT LT_252 INTO DATA(LS_252).
    "构造导出至服务器的动态内表的表字段
    CLEAR :GS_FIELD .
    GS_FIELD-FIELDNAME = LS_252-REF_FIELD .
    GS_FIELD-REF_TABLE = LS_252-REF_TABLE .
    GS_FIELD-REF_FIELD = LS_252-REF_FIELD .
    APPEND GS_FIELD TO GT_FIELD .
    IF P_LOCA = 'X' .
    "构造导出至本地的动态内表的表字段  暂时用长度128
    CLEAR :GS_FIELD .
    GS_FIELD-FIELDNAME = LS_252-REF_FIELD .
    GS_FIELD-INTTYPE   = 'C' .
    GS_FIELD-INTLEN    = '128' .
    APPEND GS_FIELD TO GT_FIELD2 .
    ENDIF.
    "构造动态取值的select字段
    IF LV_SELEA IS INITIAL.
      LV_SELEA = LV_SELEA && LS_252-REF_TABLE && '~' && LS_252-REF_FIELD .
    ELSE .
      LV_SELEA = LV_SELEA && ','  && LS_252-REF_TABLE && '~' && LS_252-REF_FIELD .
    ENDIF.
  ENDLOOP .
  "调用方法生成动态内表存储取出的数据
  CALL METHOD CL_ALV_TABLE_CREATE=>CREATE_DYNAMIC_TABLE
    EXPORTING
      IT_FIELDCATALOG = GT_FIELD
    IMPORTING
      EP_TABLE        = DY_TABLE.
  ASSIGN DY_TABLE->* TO <ITAB>. "将指针分配给动态内表
  CREATE DATA DY_LINE LIKE LINE OF <ITAB>.
  ASSIGN DY_LINE->* TO <WA>.  "将指针分配给动态内表行

  IF P_LOCA = 'X'.
  CALL METHOD CL_ALV_TABLE_CREATE=>CREATE_DYNAMIC_TABLE
    EXPORTING
      IT_FIELDCATALOG = GT_FIELD2
    IMPORTING
      EP_TABLE        = DY_TABLE2.
  ASSIGN DY_TABLE2->* TO <ITAB2>. "将指针分配给动态内表
  CREATE DATA DY_LINE LIKE LINE OF <ITAB2>.
  ASSIGN DY_LINE->* TO <WA2>.  "将指针分配给动态内表行
  ENDIF.
  "动态取出数据
  IF LV_SELEA IS NOT INITIAL.
    SELECT (LV_SELEA) FROM BKPF
       INNER JOIN BSEG ON BKPF~BUKRS = BSEG~BUKRS AND BKPF~BELNR = BSEG~BELNR AND BKPF~GJAHR = BSEG~GJAHR"
       WHERE BKPF~BUKRS IN @S_BUKRS
       AND   BKPF~BLART IN @S_BLART
       AND   BKPF~BELNR IN @S_BELNR
       AND   BSEG~HKONT IN @S_HKONT
       AND   BKPF~BUDAT IN @S_BUDAT
    INTO  TABLE @<ITAB> .
  ENDIF.
  IF <ITAB> IS INITIAL.
    MESSAGE '未找到数据' TYPE 'S' DISPLAY LIKE 'E' .
  ENDIF.
  CHECK <ITAB> IS NOT INITIAL .
  DATA: LV_FILE TYPE RLGRAP-FILENAME.
  DATA: LV_TIME TYPE C .
  DATA: p_filename TYPE STRING .
  DATA: LV_ASSGIN TYPE C LENGTH 30 .
  DATA: LV_INDEX TYPE SY-INDEX .
  SELECT * FROM ZTFI252 INTO TABLE @DATA(LT_252B) .
  SORT LT_252B BY  XH .
  DELETE ADJACENT DUPLICATES FROM LT_252B COMPARING XH .
  ""取表头描述
  SELECT TABNAME, FIELDNAME, DDTEXT ,XH FROM DD03M AS A
    INNER JOIN  @LT_252B AS B ON TABNAME = B~REF_TABLE AND FIELDNAME = B~REF_FIELD  AND DDLANGUAGE = '1'
  INTO TABLE @DATA(LT_03M) .
  SORT LT_03M BY  XH .
  DELETE ADJACENT DUPLICATES FROM LT_03M COMPARING XH .
  LOOP AT LT_03M INTO DATA(LS_03M).
    IF P_SERV = 'X' . "构造导出至服务器数据表头
      CONCATENATE LINE1 LS_03M-DDTEXT ',' INTO LINE1 . "每个数据后面加个逗号,excel就会换单元格
    ELSEIF P_LOCA = 'X' ."构造导出至本地数据表头
      CONCATENATE  '<WA2>-'  LS_03M-FIELDNAME INTO LV_ASSGIN .
      ASSIGN (LV_ASSGIN) TO FIELD-SYMBOL(<FS_VALUE>) .
      IF <FS_VALUE> IS ASSIGNED .
        <FS_VALUE> = LS_03M-DDTEXT .
      ENDIF.
      UNASSIGN <FS_VALUE> .
      lt_fieldname-name = LS_03M-DDTEXT.   "EXCEL表头  LOOP p_table的组件,并依次将字段中文描述赋值给lt_fieldname
      APPEND lt_fieldname.
    ENDIF.
  ENDLOOP.
 " APPEND <WA2> TO <ITAB2> .""函数自带表头传入参数lt_fieldname就会有表头

  DO . ""
    LV_TIME = SY-INDEX .
    CONCATENATE P_FILE  S_BUDAT-LOW '-' S_BUDAT-HIGH '-' SY-UZEIT LV_TIME '.CSV' INTO LV_FILE. "服务器保存的目录和文件名
    CONCATENATE LINES LINE1 INTO LINES . "将表头添加进表
    CLEAR : LS_BKPF ,LS_BSEG .
    DO P_NUMB TIMES. "按照凭证上的次数生成文件
      UNASSIGN <WA> .
      LV_INDEX = LV_INDEX   1 .
      READ TABLE <ITAB> ASSIGNING <WA> INDEX LV_INDEX  .
      IF <WA> IS ASSIGNED .
        IF P_SERV = 'X'. "构造导出到服务器的数据
          LOOP AT LT_252B INTO LS_252.
            CONCATENATE  '<WA>-'  LS_252-REF_FIELD INTO LV_ASSGIN .
            ASSIGN (LV_ASSGIN) TO <FS_VALUE> .
            IF <FS_VALUE> IS ASSIGNED.
              LV_STR = <FS_VALUE> . "可能有金额字段,需要转化位字符串
              CONCATENATE LINE LV_STR ',' INTO LINE . "每个数据后面加个逗号,excel就会换单元格
            ENDIF.
          ENDLOOP.
          UNASSIGN <WA> .
          CONCATENATE  LINES   CL_ABAP_CHAR_UTILITIES=>CR_LF(1)  LINE INTO LINES. "将换行符和这行数据加入表数据中
          CLEAR LINE .
        ELSEIF P_LOCA = 'X'.
          MOVE-CORRESPONDING <WA> TO <WA2> .
          APPEND <WA2> TO <ITAB2> .
          UNASSIGN: <WA> .
          CLEAR :<WA2> .
        ENDIF.
      ELSE .
        EXIT .
      ENDIF.
    ENDDO.
    "每循环一次导出一次数据
    IF  P_SERV = 'X'.
      CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
        EXPORTING
          TEXT     = LINES
          ENCODING = '8404'
        IMPORTING
          BUFFER   = LD_XSTRING
        EXCEPTIONS
          FAILED   = 1
          OTHERS   = 2.

      "  DATA(utf8) = cl_abap_codepage=>convert_to( LINES ). "如果要使用AL11直接下载后台跑出的文件到本地用这个方法转码,而不是上面那个函数
      OPEN DATASET LV_FILE FOR OUTPUT IN BINARY MODE ."TEXT  MODE encoding UTF-8
      " OPEN DATASET LV_FILE FOR OUTPUT IN TEXT MODE ENCODING UTF-8 WITH BYTE-ORDER MARK."TEXT  MODE encoding UTF-8
      IF SY-SUBRC = 0.
        TRANSFER LD_XSTRING TO LV_FILE.
        "   TRANSFER LD_XSTRING TO LV_FILE.
        CLOSE DATASET LV_FILE.
      ENDIF .
      CLEAR : LINE,LINES .
    ELSEIF P_LOCA = 'X' .
      CONCATENATE P_FILE  S_BUDAT-LOW '-' S_BUDAT-HIGH '-' SY-UZEIT LV_TIME '.XLS' INTO LV_FILE. "服务器保存的目录和文件名
      p_filename = LV_FILE .
      CALL FUNCTION 'GUI_DOWNLOAD'
        EXPORTING
          filename   = p_filename
          filetype   = 'DAT' "ASC格式 1000- 不会显示为 -1000 DBF格式 字符前空格 前导0不会显示
          codepage   = '8404' "四位字符集代码 可通过表TCP00A,查询对应字符集代码
        TABLES
          data_tab   = <ITAB2>
          fieldnames = lt_fieldname.
        .
      CLEAR: <ITAB2>.

    ENDIF.
    IF <WA> IS NOT ASSIGNED .
      EXIT .
    ENDIF.
  ENDDO.
  CLEAR : LV_INDEX .
ENDFORM .

FORM FRM_F4_P_FILE .
  DATA LV_NAME TYPE C LENGTH 30 .
  LV_NAME = SY-DATUM && SY-UZEIT .
  CALL FUNCTION 'WS_FILENAME_GET' "用于选择本地文件,获取本地文件的路径
    EXPORTING
      DEF_FILENAME     = LV_NAME  "默认的文件名称
*     DEF_PATH         = ' '  "默认的文件地址
*     MASK             = ',EXCEL.XLS,*.XLSX,*.XLS,TEXT.TXT,*.TXT.'
      MODE             = 'O'      "S 保存  O 打开
      TITLE            = '选择文件'  "窗口的显示名称
    IMPORTING                        "传出给选择屏幕上的文本框
      FILENAME         = P_FILE
*     RC               =
    EXCEPTIONS
      INV_WINSYS       = 1
      NO_BATCH         = 2
      SELECTION_CANCEL = 3
      SELECTION_ERROR  = 4
      OTHERS           = 5.
  IF SY-SUBRC <> 0.
* Implement suitable error handling here
  ENDIF.
ENDFORM .

总结:这个程序导出速度很快,主要有以下原因

1.没有多余的取值字段,都是按照用户维护,活动内表,活动取值,不浪费算力

2.可以服务器后台处理,比本机快

3.直接sap底层转码,构造文件,速度很快

扩展:如果想批导出其他的表而不是bkpf及bseg,可以看着修改选择屏幕以及动态取出表数据的那段select就行了

0 人点赞