通过几个例子,由浅入深讲解 ABAP 动态编程。ABAP 动态编程主要通过 RTTS (Runtime Type Services) 来实现,包括 RTTI 和 RTTC:
- 运行时类型标识(RTTI) – 提供在运行时获取数据对象的类型定义的方法。
- 运行时类型创建(RTTC) – 提供在运行时使用任何类型定义创建数据对象的方法。
动态程序的基本要素
第一个例子主要演示构建程序的最基本要素。
第一步:创建 structure descriptor 和 table descriptor,主要用于后面动态构建内表的结构:
data struct_descr type ref to cl_abap_datadescr.
data table_descr type ref to cl_abap_tabledescr.struct_descr ?= cl_abap_structdescr=>describe_by_name( 'SFLIGHTS' ).
table_descr ?= cl_abap_tabledescr=>create( p_line_type = struct_descr ).
第二步:基于 table descriptor 创建泛型的 data reference object:
data table_dref type ref to data.
create data table_dref type handle table_descr.
第三步:使用 field symbol,将 table reference object 指向 field symbol。
field-symbols <table> type any table.
assign table_dref->* to <table>.
这样,就动态创建了一个内表 <table>。第一个完整的例子:
report z_dynamic_table_01.data gr_alv type ref to cl_salv_table.data struct_descr type ref to cl_abap_datadescr.
data table_descr type ref to cl_abap_tabledescr.struct_descr ?= cl_abap_structdescr=>describe_by_name( 'SFLIGHTS' ).
table_descr ?= cl_abap_tabledescr=>create( p_line_type = struct_descr ).data table_dref type ref to data.
create data table_dref type handle table_descr.field-symbols <table> type any table.
assign table_dref->* to <table>.select * from sflights up to 10 rows into corresponding fields of table <table>.try.call method cl_salv_table=>factoryimportingr_salv_table = gr_alvchangingt_table = <table>.catch cx_salv_msg .
endtry.gr_alv->display( ).
动态程序的灵活性
如果只是从 SFLIGHTS 表获取数据,当然没必要使用动态程序,因为它让程序更加复杂。我们对上面的程序进行修改,支持在选择屏幕中输入任意表名,都能够在 ALV 中显示。
report z_dynamic_table_02.data gr_alv type ref to cl_salv_table.data struct_descr type ref to cl_abap_datadescr.
data table_descr type ref to cl_abap_tabledescr.parameters tab_name type dd02l-tabname.start-of-selection.struct_descr ?= cl_abap_structdescr=>describe_by_name( tab_name ).table_descr ?= cl_abap_tabledescr=>create( p_line_type = struct_descr ).data table_dref type ref to data.create data table_dref type handle table_descr.field-symbols <table> type any table.assign table_dref->* to <table>.select * from (tab_name) up to 10 rows into corresponding fields of table <table>.try.call method cl_salv_table=>factoryimportingr_salv_table = gr_alvchangingt_table = <table>.catch cx_salv_msg .endtry.gr_alv->display( ).
构建动态程序所需的字段
第一个例子主要是演示动态程序的基本要素,一般情况下,我们不需要获取一个数据表的所有字段,这样就需要创建需要的字段。下面的示例演示了创建字段的方法。示例仍然从 sflights 中获取数据,然后在 ALV 中显示。只需要 CARRID, CONNID, CARRNAME 和 FLDATE 四个字段。
report z_dynamic_table_03.data gr_alv type ref to cl_salv_table.data gs_component type cl_abap_structdescr=>component.
data gt_component type cl_abap_structdescr=>component_table.data struct_descr type ref to cl_abap_datadescr.
data table_descr type ref to cl_abap_tabledescr.data carrid type sflights-carrid.
data connid type sflights-connid.
data carrname type sflights-carrname.
data fldate type sflights-fldate.try.gs_component-name = 'CARRID'.gs_component-type ?= cl_abap_datadescr=>describe_by_data( carrid ).append gs_component to gt_component.gs_component-name = 'CONNID'.gs_component-type ?= cl_abap_datadescr=>describe_by_data( connid ).append gs_component to gt_component.gs_component-name = 'CARRNAME'.gs_component-type ?= cl_abap_datadescr=>describe_by_data( carrname ).append gs_component to gt_component.gs_component-name = 'FLDATE'.gs_component-type ?= cl_abap_datadescr=>describe_by_data( fldate ).append gs_component to gt_component.struct_descr ?= cl_abap_structdescr=>create( p_components = gt_component ).table_descr ?= cl_abap_tabledescr=>create( p_line_type = struct_descr ).data table_dref type ref to data.create data table_dref type handle table_descr.field-symbols <table> type any table.assign table_dref->* to <table>." Select data from database and fill dynamically created tableselect * from sflights up to 10 rows into corresponding fields of table <table>.try.call method cl_salv_table=>factoryimportingr_salv_table = gr_alvchangingt_table = <table>.catch cx_salv_msg .endtry.gr_alv->display( ).catch cx_root.
endtry.
动态程序实现行转列
最后给出一个稍微综合一点的示例,对于给定的数据:
实现行转列,并在 ALV 中显示:
完整代码:
report z_dynamic_table_rtts.type-pools: slis .types:begin of gfirst_typ,vend(6) type c,month(5) type c,amt type i,end of gfirst_typ .* RTTS declarations
data struct_descr type ref to cl_abap_datadescr.
data table_descr type ref to cl_abap_tabledescr.
data gs_component type cl_abap_structdescr=>component.
data gt_component type cl_abap_structdescr=>component_table.* Dynamice table declarations
data table_dref type ref to data.
data line_dref type ref to data.* Field symbol decalrations
field-symbols: <gfs_line>, <gfs_line1>.
field-symbols: <gfs_table> type standard table, <fld>.data it_zdemo type standard table of gfirst_typ.
data wa_zdemo like line of it_zdemo.* SALV declarionts.
data lo_cols type ref to cl_salv_columns.
data lo_salv_table type ref to cl_salv_table.
data lo_column type ref to cl_salv_column.
data col_name(30) type c.
data col_desc(20) type c.start-of-selection.* Populate the initial input table.
* Usually this input table contents will be popluated at run time,
* which raises the requirement of dynamic table.
* The talbe contens are filled here for illustration purpose.perform fill_table using:'V100' 'JAN' '100','V100' 'FEB' '250','V200' 'FEB' '200','V300' 'FEB' '150','V200' 'MAR' '250','V300' 'MAR' '300','V100' 'APR' '200','V100' 'MAY' '100','V200' 'MAY' '50','V300' 'MAY' '125','V400' 'MAY' '475'.* Write the datawrite : / 'Initial internal table'.write : /(6) 'Vendor', (12) 'Month', (3) 'Amt'.loop at it_zdemo into wa_zdemo.write : / wa_zdemo-vend, wa_zdemo-month, wa_zdemo-amt.clear wa_zdemo.endloop.* Create structure of dyanmic internal table.gs_component-name = 'VEND'.gs_component-type ?= cl_abap_datadescr=>describe_by_data( wa_zdemo-vend ).append gs_component to gt_component .* Loop throught the internal table creating a column for every distinct monthloop at it_zdemo into wa_zdemo.
* Search the component table if the month column already exists.read table gt_component into gs_component with key name = wa_zdemo-month.if sy-subrc ne 0.
* The name of the column would be the month
* and the data type would be the same as the amount filed of intenal talegs_component-name = wa_zdemo-month.gs_component-type ?= cl_abap_datadescr=>describe_by_data( wa_zdemo-amt ).append gs_component to gt_component.endif.clear: gs_component, wa_zdemo.endloop.struct_descr ?= cl_abap_structdescr=>create( p_components = gt_component ).table_descr ?= cl_abap_tabledescr=>create( p_line_type = struct_descr ).create data table_dref type handle table_descr.create data line_dref type handle struct_descr.assign table_dref->* to <gfs_table>.assign line_dref->* to <gfs_line>.* Fill vendor fieldloop at it_zdemo into wa_zdemo.read table <gfs_table> into <gfs_line> with key ('VEND') = wa_zdemo-vend.if sy-subrc ne 0.assign component 'VEND' of structure <gfs_line> to <fld>.<fld> = wa_zdemo-vend.append <fld> to <gfs_table>.endif.clear wa_zdemo.endloop.* Aggregate data.loop at it_zdemo into wa_zdemo.read table <gfs_table> assigning <gfs_line> with key ('VEND') = wa_zdemo-vend.loop at gt_component into gs_component.if gs_component-name = wa_zdemo-month.if <fld> is assigned.unassign <fld>.endif.assign component gs_component-name of structure <gfs_line> to <fld>.<fld> = <fld> + wa_zdemo-amt.endif.endloop.clear wa_zdemo.endloop.* ALV showtry.cl_salv_table=>factory(importingr_salv_table = lo_salv_tablechangingt_table = <gfs_table>).catch cx_salv_msg.endtry.* Get columns objectlo_cols = lo_salv_table->get_columns( ).* Individual column namesloop at gt_component into gs_component.try.col_name = gs_component-name.lo_column = lo_cols->get_column( col_name ).if col_name = 'VEND'.col_desc = 'Vendor'.else.concatenate col_name '''13' into col_desc.endif.lo_column->set_medium_text( col_desc ).lo_column->set_output_length( 10 ).catch cx_salv_not_found.endtry.endloop.* Display tablelo_salv_table->display( ).*&---------------------------------------------------------------------*
*& Form fill_table
*&---------------------------------------------------------------------*
form fill_table using p_fld1 type gfirst_typ-vendp_fld2 type gfirst_typ-monthp_fld3 type gfirst_typ-amt.clear wa_zdemo.wa_zdemo-vend = p_fld1.wa_zdemo-month = p_fld2.wa_zdemo-amt = p_fld3.append wa_zdemo to it_zdemo.endform. "fill_table