ABAP语言的动态程序
通过几个例子,由浅入深讲解 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=>factory
importing
r_salv_table = gr_alv
changing
t_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=>factory
importing
r_salv_table = gr_alv
changing
t_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 table
select * from sflights up to 10 rows into corresponding fields of table <table>.
try.
call method cl_salv_table=>factory
importing
r_salv_table = gr_alv
changing
t_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 data
write : / '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 month
loop 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 tale
gs_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 field
loop 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 show
try.
cl_salv_table=>factory(
importing
r_salv_table = lo_salv_table
changing
t_table = <gfs_table>
).
catch cx_salv_msg.
endtry.
* Get columns object
lo_cols = lo_salv_table->get_columns( ).
* Individual column names
loop 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 table
lo_salv_table->display( ).
*&---------------------------------------------------------------------*
*& Form fill_table
*&---------------------------------------------------------------------*
form fill_table using p_fld1 type gfirst_typ-vend
p_fld2 type gfirst_typ-month
p_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