ABAP 创建动态内表查询任意透明表并进行ALV展示

该功能可以根据用户在选择屏幕输入的表名、行数,动态创建内表、动态查询,然后根据获取到的数据进行ALV展示。

代码如下:

java 复制代码
REPORT ZMYDEMO001.

DATA:
  GV_TABNAME      TYPE TABNAME,
  GR_DATA_TABLE   TYPE REF TO DATA,
  LO_TYPEDESCR    TYPE REF TO CL_ABAP_TYPEDESCR,
  GO_STRUCT_DESCR TYPE REF TO CL_ABAP_STRUCTDESCR,
  GO_TABLE_DESCR  TYPE REF TO CL_ABAP_TABLEDESCR,
  LV_LINE_COUNT   TYPE I,
  LV_TABCLASS     TYPE TABCLASS,
  LV_DOMNAME      TYPE DOMNAME,
  LV_SCRTEXT_S    TYPE SCRTEXT_S.

* 字段描述存储(全版本兼容结构)
DATA: LT_DDFIELD TYPE STANDARD TABLE OF DFIES,
      LS_DDFIELD TYPE DFIES.

DATA: LV_JSON_STRING TYPE STRING.

*----------------------------------------------------------------------*
* 字段符号声明
*----------------------------------------------------------------------*
FIELD-SYMBOLS:
  <FT_DATA> TYPE ANY TABLE,
  <FS_LINE> TYPE ANY,
  <FS_FIELD> TYPE ANY.

*----------------------------------------------------------------------*
* 选择屏幕
*----------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK B1.
  PARAMETERS:
    P_NAME    TYPE TABNAME OBLIGATORY,
    P_MAX     TYPE I DEFAULT 500.
SELECTION-SCREEN END OF BLOCK B1.

*----------------------------------------------------------------------*
* 主程序
*----------------------------------------------------------------------*
START-OF-SELECTION.

  " 1. 表名转大写
  GV_TABNAME = P_NAME.
  TRANSLATE GV_TABNAME TO UPPER CASE.

  " 2. 验证表存在性和类型
  SELECT SINGLE TABCLASS
    INTO LV_TABCLASS
    FROM DD02L
   WHERE TABNAME = GV_TABNAME
     AND AS4LOCAL = 'A'
     AND AS4VERS = '0000'.

  IF SY-SUBRC <> 0.
    MESSAGE '错误:表不存在或未激活' TYPE 'E'.
  ENDIF.

  IF LV_TABCLASS <> 'TRANSP' AND LV_TABCLASS <> 'VIEW'.
    MESSAGE '错误:只支持查询透明表和视图' TYPE 'E'.
  ENDIF.

  " 3. 读取字段描述(内置类型+数据元素双保险)(重要步骤)
  PERFORM GET_FIELD_DESC_ULTIMATE USING GV_TABNAME CHANGING LT_DDFIELD.

  " 4. 动态创建内表(核心步骤)
  TRY.
      LO_TYPEDESCR = CL_ABAP_STRUCTDESCR=>DESCRIBE_BY_NAME( GV_TABNAME ).
      GO_STRUCT_DESCR ?= LO_TYPEDESCR.
      GO_TABLE_DESCR = CL_ABAP_TABLEDESCR=>CREATE(
                         P_LINE_TYPE  = GO_STRUCT_DESCR
                         P_TABLE_KIND = CL_ABAP_TABLEDESCR=>TABLEKIND_STD ).
      CREATE DATA GR_DATA_TABLE TYPE HANDLE GO_TABLE_DESCR.
    CATCH CX_ROOT INTO DATA(LX_CREATE).
      MESSAGE '动态创建内表失败: ' && LX_CREATE->GET_TEXT( ) TYPE 'E'.
  ENDTRY.

  " 5. 分配字段符号
  ASSIGN GR_DATA_TABLE->* TO <FT_DATA>.
  IF <FT_DATA> IS NOT ASSIGNED.
    MESSAGE '错误:无法分配内表字段符号' TYPE 'E'.
  ENDIF.

  " 6. 动态查询数据
  IF P_MAX <= 0 OR P_MAX > 2000.
    P_MAX = 500.
  ENDIF.

  TRY.
      SELECT * UP TO P_MAX ROWS INTO TABLE <FT_DATA> FROM (GV_TABNAME).
      IF SY-SUBRC <> 0.
        MESSAGE 'SQL查询失败: 没有找到数据' TYPE 'I'.
      ENDIF.
    CATCH CX_ROOT INTO DATA(LX_SELECT).
      MESSAGE 'SQL查询失败: ' && LX_SELECT->GET_TEXT( ) TYPE 'E'.
  ENDTRY.

  "ALV展示
  PERFORM SHOW_DATA_ALV.

*----------------------------------------------------------------------*
* 子例程1:读取字段描述(内置类型+数据元素全支持)
*----------------------------------------------------------------------*
FORM GET_FIELD_DESC_ULTIMATE USING IV_TAB TYPE TABNAME CHANGING CT_FLD TYPE DFIES_TAB.
  CALL FUNCTION 'DDIF_FIELDINFO_GET'
    EXPORTING
      TABNAME   = IV_TAB
      LANGU     = SY-LANGU
      ALL_TYPES = 'X'
    TABLES
      DFIES_TAB = CT_FLD
    EXCEPTIONS
      OTHERS    = 1.

  IF SY-SUBRC <> 0.
    MESSAGE '获取字段信息失败' TYPE 'E'.
  ENDIF.

  " 为内置类型字段补全描述
  LOOP AT CT_FLD INTO LS_DDFIELD.
    IF LS_DDFIELD-ROLLNAME IS INITIAL.
      " 尝试1:从DD03T获取表字段专属文本
      SELECT SINGLE DDTEXT
        INTO LS_DDFIELD-SCRTEXT_S
        FROM DD03T
       WHERE TABNAME  = IV_TAB
         AND FIELDNAME = LS_DDFIELD-FIELDNAME
         AND DDLANGUAGE = SY-LANGU.

      " 尝试2:从域文本表获取描述
      IF LS_DDFIELD-SCRTEXT_S IS INITIAL AND LS_DDFIELD-DOMNAME IS NOT INITIAL.
        SELECT SINGLE DDTEXT
          INTO LS_DDFIELD-SCRTEXT_S
          FROM DD07T
         WHERE DOMNAME  = LS_DDFIELD-DOMNAME
           AND DDLANGUAGE = SY-LANGU.
      ENDIF.

      " 尝试3:兜底方案
      IF LS_DDFIELD-SCRTEXT_S IS INITIAL.
        CONCATENATE LS_DDFIELD-FIELDNAME '(' LS_DDFIELD-INTTYPE ')' INTO LS_DDFIELD-SCRTEXT_S.
      ENDIF.

      MODIFY CT_FLD FROM LS_DDFIELD.
    ENDIF.
  ENDLOOP.
ENDFORM.

*----------------------------------------------------------------------*
* 子例程2:ALV显示
*----------------------------------------------------------------------*
FORM SHOW_DATA_ALV.
  DATA: LO_ALV     TYPE REF TO CL_SALV_TABLE,
        LO_COLS    TYPE REF TO CL_SALV_COLUMNS_TABLE,
        LO_COL     TYPE REF TO CL_SALV_COLUMN_TABLE,
        LV_TEXT    TYPE SCRTEXT_S.

  TRY.
      CL_SALV_TABLE=>FACTORY( IMPORTING R_SALV_TABLE = LO_ALV CHANGING T_TABLE = <FT_DATA> ).
      LO_COLS = LO_ALV->GET_COLUMNS( ).
      LO_COLS->SET_OPTIMIZE( ABAP_TRUE ).

      LOOP AT LT_DDFIELD INTO LS_DDFIELD.
        TRY.
            LO_COL ?= LO_COLS->GET_COLUMN( LS_DDFIELD-FIELDNAME ).

            IF LS_DDFIELD-SCRTEXT_S IS NOT INITIAL.
              LV_TEXT = LS_DDFIELD-SCRTEXT_S.
            ELSE.
              LV_TEXT = LS_DDFIELD-FIELDNAME.
            ENDIF.

            LO_COL->SET_SHORT_TEXT( LV_TEXT ).
          CATCH CX_ROOT.
            CONTINUE.
        ENDTRY.
      ENDLOOP.

      LO_ALV->DISPLAY( ).
    CATCH CX_ROOT.
      " ALV异常时降级输出
      LOOP AT <FT_DATA> ASSIGNING <FS_LINE>.
        WRITE: / <FS_LINE>.
      ENDLOOP.
  ENDTRY.
ENDFORM.