Using Secondary keys to Access the Same Internal Table in Different Ways
如下面代码中,我们需要根据number或name读取数据,number定义成hash表的key,因此如果有值将采用number 作为primary key读取,如果number为空将采用name作为second key读取。
DATA:
table_of_monsters TYPE HASHED TABLE OF ztsm_monsters WITH UNIQUE KEY monster_number
WITH NON-UNIQUE SORTED KEY sort COMPONENTS name .
IF id_monster_number IS NOT INITIAL.
READ TABLE table_of_monsters INTO es_monsters
WITH TABLE KEY monster_number = id_monster_number.
ELSE.
READ TABLE table_of_monsters INTO es_monsters
WITH KEY sort COMPONENTS name = id_name ##primkey[sort].
ENDIF.
如下代码将monster_number bed_number定义为hash key并将monster_number 定义为second key,如果只有monster_number,首先通过read table TRANSPORTING NO FIELDS 检查是否存在,由于定义了second key,内表将可以根据second key做LOOP及read binary search。monster_beds可以作为hash table也可以作为sorted table。
DATA:
monster_beds TYPE HASHED TABLE OF ztvc_monstr_beds
WITH UNIQUE KEY monster_number bed_number
WITH NON-UNIQUE SORTED KEY sort COMPONENTS monster_number,
start_point TYPE sy-tabix.
IF id_bed_number IS NOT INITIAL.
READ TABLE monster_beds INTO es_beds
WITH TABLE KEY monster_number = id_monster_number
bed_number = id_bed_number.
ELSE.
"No bed, return all bed data for monster
READ TABLE monster_beds TRANSPORTING NO FIELDS
WITH KEY sort COMPONENTS monster_number = id_monster_number ##primkey[sort].
CHECK sy-subrc = 0.
start_point = sy-tabix.
LOOP AT monster_beds INTO DATA(monster_bed_record)
USING KEY sort FROM start_point.
IF monster_bed_record-monster_number GT id_monster_number.
EXIT."From Inner Loop
ENDIF.
CHECK monster_bed_record-monster_number = id_monster_number.
APPEND monster_bed_record TO et_monster_beds.
ENDLOOP.
SORT et_monster_beds BY monster_number bed_number.
ENDIF.
Table Work Areas
以前我们一般先定义internal table与work area,7.4以后,我们可以在对内表的操作过程中定义work area及field symbol。
DATA: monster_number TYPE zde_monster_number VALUE '000000001',
table_of_monsters TYPE STANDARD TABLE OF ztsm_monsters.
READ TABLE table_of_monsters WITH KEY monster_number = monster_number
INTO DATA(monster_details).
LOOP AT table_of_monsters INTO DATA(monster_details2).
ENDLOOP.
READ TABLE table_of_monsters WITH KEY monster_number = monster_number
ASSIGNING FIELD-SYMBOL().
LOOP AT table_of_monsters ASSIGNING FIELD-SYMBOL().
ENDLOOP.
Reading from a Table
7.4以前Read Table的形式如下
READ TABLE table_of_monsters INTO monster_details
WITH KEY name = monster_name.
monster = zcl_monster=>factory( monster_details-monster_number ).
7.4以后可以内表后面跟[]来替代关键字read table与with key
monster = zcl_monster=>factory( table_of_monsters[ monster_name ]-monster_number ).
我们可以结合string如下使用代码,但这里有个问题,如果访问内表没取到值,将会有异常出现。
zcl_bc_screen_message=>output(
|{ monster_name } s Monster Number is { table_of_monsters[ monster_name ]-monster_number }| ).
可以用如下代码来改善,添加OPTIONAL或DEFAULT来避免异常产生。此时如果为空将返回空值。
zcl_bc_screen_message=>output(
|{ monster_name } s Monster Number is { VALUE #( table_of_monsters[ monster_name ]-monster_number OPTIONAL ) }| ).
zcl_bc_screen_message=>output(
|{ monster_name } s Monster Number is { VALUE #( table_of_monsters[ monster_name ]-monster_number DEFAULT '9999999999' ) }| ).
CORRESPONDING for Normal Internal Tables
通常我们用MOVE-CORRESPONDING来将结构赋值到另一个结构,当对内表赋值时,代码如下
DATA: green_monsters TYPE STANDARD TABLE OF ztsm_monsters,
blue_monsters TYPE STANDARD TABLE OF ztsm_monsters.
FIELD-SYMBOLS:
LIKE LINE OF green_monsters,
LIKE LINE OF blue_monsters.
LOOP AT green_monsters ASSIGNING .
APPEND INITIAL LINE TO blue_monsters
ASSIGNING .
MOVE-CORRESPONDING TO .
CLEAR -evilness.
-people_scared =
-most_peasants_scared.
ENDLOOP.
7.4后可以用CORRESPONDING来对内表赋值,后面跟的#表示创建的目标表与原始表列相同,Except及Mapping修改了相关的字段。
green_monsters = CORRESPONDING #(
blue_monsters
MAPPING people_scared = most_peasants_scared
EXCEPT evilness ).
MOVE-CORRESPONDING for Internal Tables with Deep Structures
如下定义内表,european_result 与 us_result都是一个字段加一个内表结构字段,当进行赋值时,虽然内字段的结构不一样,但因为都叫t_result,因此us中lockbox将被赋值上iban的值。
TYPES: BEGIN OF l_typ_european_monsters,
monster_name TYPE string,
monster_iban_code TYPE string,
END OF l_typ_european_monsters.
TYPES: l_tt_european_monsters TYPE HASHED TABLE OF l_typ_european_monsters
WITH UNIQUE KEY monster_name.
DATA: iban_code_record TYPE l_typ_european_monsters.
TYPES: BEGIN OF l_typ_european_results,
laboratory TYPE string,
t_result TYPE l_tt_european_monsters,
END OF l_typ_european_results.
TYPES: l_tt_european_results TYPE STANDARD TABLE OF l_typ_european_monsters.
DATA: european_result TYPE l_typ_european_results,
european_results TYPE l_tt_european_results.
TYPES: BEGIN OF l_typ_us_monsters,
monster_name TYPE string,
monster_lockbox_code TYPE string,
END OF l_typ_us_monsters.
TYPES: l_tt_us_monsters TYPE HASHED TABLE OF l_typ_us_monsters
WITH UNIQUE KEY monster_name.
TYPES: BEGIN OF l_typ_us_results,
laboratory TYPE string,
t_result TYPE l_tt_us_monsters,
END OF l_typ_us_results.
TYPES: l_tt_us_results TYPE STANDARD TABLE OF l_typ_us_results.
DATA: us_result TYPE l_typ_us_results,
us_results TYPE l_tt_us_results.
*--------------------------------------------------------------------*
* Listing 2.60 Attempt at MOVE-CORRESPONDING
*--------------------------------------------------------------------*
european_result-laboratory = 'SECRET LABORATORY 51'.
iban_code_record-monster_name = 'FRED'.
iban_code_record-monster_iban_code = 'AL47212110090000000235698741'.
INSERT iban_code_record INTO TABLE european_result-t_result.
MOVE-CORRESPONDING european_result TO us_result.
当两个内表结构的字段不一致时,以前我们赋值要用Loop,MOVE-CORRESPONDING结构
LOOP AT european_results ASSIGNING FIELD-SYMBOL().
APPEND INITIAL LINE TO us_results ASSIGNING FIELD-SYMBOL().
MOVE-CORRESPONDING TO .
ENDLOOP.
7.4后可以直接MOVE-CORRESPONDING内部操作MOVE-CORRESPONDING european_results TO us_results.
,但这个还是没解决上面t_result内表结构字段不同赋值问题。因此7.4引入了MOVE -CORRESPONDING EXPANDING NESTED TABLES
,该语句赋值时,首先删除目标表的记录,然后赋值时会校验t_result内表结构字段,如果该字段不同则该字段将不赋值。MOVE-CORRESPONDING KEEPING TARGET LINES
可以保留原内表的记录,类似于APPEND LIN ES OF table1 TO table2
,MOVE -CORRESPONDING EXPANDING NESTED TABLES KEEPING TARGET LINES
可以将两者结合起来。
Dynamic MOVE-CORRESPONDING
7.4后不推荐用MOVE-CORRESPONDING,尤其是HANA数据库,可以用如下方法动态匹配。LEVEL参数用在匹配deep structure,kind参数可以填EXCEPT等选项,dstname为目标表的字段,srcname为相应的值,通过cl_abap_corresponding的create及execute方法执行。
DATA: mapping_record TYPE cl_abap_corresponding=>mapping_info,
mapping_table TYPE cl_abap_corresponding=>mapping_table.
mapping_record-level = 0.
mapping_record-kind = cl_abap_corresponding=>mapping_component.
mapping_record-dstname = 'BEST_BRAIN_01'.
IF is_monster_header-hat_size > 5.
mapping_record-srcname = 'BIGGEST_BRAIN'.
ELSE.
mapping_record-srcname = 'SMALLEST_BRAIN'.
ENDIF.
APPEND mapping_record TO mapping_table.
mapping_record-dstname = 'BEST_BRAIN_02'.
IF id_monster_usage = 'MORTGAGE_SALESMAN'.
mapping_record-srcname = 'EVILEST_BRAIN'.
ELSEIF id_monster_usage = 'MORRIS_DANCER'.
mapping_record-srcname = 'WEIRDEST_BRAIN'.
ENDIF.
APPEND mapping_record TO mapping_table.
TRY.
DATA(dynamic_mapper) =
cl_abap_corresponding=>create(
source = is_possible_brains
destination = rs_best_brains
mapping = mapping_table ).
dynamic_mapper->execute(
EXPORTING source = is_possible_brains
CHANGING destination = rs_best_brains ).
CATCH cx_corr_dyn_error.
"Fatal Error
ENDTRY.
New Functions for Common Internal Table Tasks
以前我们获取行数通过sy-tabix。
READ TABLE table_of_monsters WITH KEY monster_number = monster_number
TRANSPORTING NO FIELDS.
IF sy-subrc = 0.
start_row = sy-tabix.
ENDIF.
现在可以用line_index来获取
DATA(start_row2) = line_index( table_of_monsters[ monster_number = monster_number ] ).
直接作为放回值使用
LOOP AT table_of_monsters FROM line_index( table_of_monsters[ monster_number = monster_number ] )
ASSIGNING FIELD-SYMBOL().
ENDLOOP.
判断是否存在也可以用line_exists来替代。
*--------------------------------------------------------------------*
* Listing 2.67 Seeing Whether an Internal Table Line Exists before 7.4
*--------------------------------------------------------------------*
READ TABLE table_of_monsters ASSIGNING
WITH KEY monster_number = monster_number.
IF sy-subrc NE 0.
APPEND INITIAL LINE TO table_of_monsters ASSIGNING .
ENDIF.
ADD 1 TO -sanity.
*--------------------------------------------------------------------*
* Listing 2.68 Seeing Whether an Internal Table Line Exists in 7.40
*--------------------------------------------------------------------*
IF line_exists( table_of_monsters[ monster_number = monster_number ] ).
READ TABLE table_of_monsters ASSIGNING
WITH KEY monster_number = monster_number.
ELSE.
APPEND INITIAL LINE TO table_of_monsters ASSIGNING .
ENDIF.
Internal Table Queries with REDUCE
REDUCE可以处理内表并放回一个结果,如下代码,reduce后面跟返回结果的类型,init定义了一个临时变量,FOR循环结束后,result将会被赋值给mad_monsters_count。
DATA: neurotic_monsters TYPE STANDARD TABLE OF ztsm_monsters.
DATA(monster) = NEW zcl_monster( ).
DATA(mad_monsters_count) = REDUCE sy-tabix(
INIT result = 0
FOR monster_details IN neurotic_monsters
NEXT result = result +
monster->is_it_mad( monster_details-monster_number ) ).
Grouping Internal Tables
7.4在Loop中引进了Group BY选项,如下代码,根据Main table中的记录,取出每个type下people scared总人数,并做附加条件判断。在外层loop中,Group有两条记录,vamp/x 与 zomb/x,通过debug可以看到外层loop只循环两次,monster->is_it_mad被调用了5次,在循环内,可以通过LOOP AT GROUP
来取出该group组合下Main table中的原始数据,然后进行相应的操作。
TYPES: tt_monsters TYPE STANDARD TABLE OF ztsm_monsters
WITH DEFAULT KEY.
DATA: monster_sub_set TYPE tt_monsters,
total_scared TYPE i.
DATA(monster) = NEW zcl_monster( ).
DATA(table_of_monsters) = VALUE tt_monsters(
( monster_number = '1' monster_type = 'VAMP' people_scared = 3 )
( monster_number = '2' monster_type = 'VAMP' people_scared = 4 )
( monster_number = '3' monster_type = 'VAMP' people_scared = 2 )
( monster_number = '4' monster_type = 'ZOMB' people_scared = 1 )
( monster_number = '5' monster_type = 'ZOMB' people_scared = 1 )
).
LOOP AT table_of_monsters INTO DATA(monster_details)
GROUP BY ( monster_type = monster_details-monster_type
is_it_crackers = monster->is_it_mad( monster_details-monster_number ) )
ASSIGNING FIELD-SYMBOL().
CHECK -is_it_crackers = abap_true.
CLEAR monster_sub_set.
LOOP AT GROUP ASSIGNING FIELD-SYMBOL().
monster_sub_set = VALUE #( BASE monster_sub_set ( ) ).
ENDLOOP.
CLEAR total_scared.
LOOP AT monster_sub_set INTO DATA(sub_set_record).
ADD sub_set_record-people_scared TO total_scared.
ENDLOOP.
WRITE:/ 'Bonkers Monsters of Type',-monster_type,' scared ',total_scared,' people'.
ENDLOOP.
Extracting One Table from Another
7.4以前我们过滤一个内表的数据到另一个时,只能通过Loop来做,7.4后引入了FILTER。但注意FILTER的内表必须有一个hash key或sorted key。
*--------------------------------------------------------------------*
* Listing 2.72 Extracting One Table from Another before 7.4
*--------------------------------------------------------------------*
DATA: all_monsters TYPE SORTED TABLE OF ztsm_monsters
WITH NON-UNIQUE KEY monster_number
WITH NON-UNIQUE SORTED KEY bonkers_ness
COMPONENTS sanity,
averagely_mad_monsters TYPE STANDARD TABLE OF ztsm_monsters,
an_averagely_mad_monster LIKE LINE OF averagely_mad_monsters.
LOOP AT all_monsters INTO DATA(monster_record)
WHERE sanity < 75.
CLEAR an_averagely_mad_monster.
MOVE-CORRESPONDING monster_record TO an_averagely_mad_monster.
APPEND an_averagely_mad_monster TO averagely_mad_monsters.
ENDLOOP."All Monsters
*--------------------------------------------------------------------*
* Listing 2.73 Extracting One Table from Another in 7.40
*--------------------------------------------------------------------*
DATA(averagely_mad_monsters2) =
FILTER #( all_monsters USING KEY bonkers_ness
WHERE sanity < 75 ).
以前我们取一些复杂组合时,经常在SELECT中用FOR ALL ENTRIES IN,现在,如果程序中前面已经读取了整的pets_of_our_monsters,然后要进行all_monsters的过滤,可以采用下面的方法。同样注意all_monsters有一个hash key或sorted key
*--------------------------------------------------------------------*
* Listing 2.74 FOR ALL ENTRIES during a Database Read
*--------------------------------------------------------------------*
DATA: monster_pets TYPE SORTED TABLE OF ztmonster_pets
WITH NON-UNIQUE KEY owner pet_number.
SELECT *
FROM ztmonster_pets
INTO CORRESPONDING FIELDS OF TABLE monster_pets
FOR ALL ENTRIES IN all_monsters
WHERE owner = all_monsters-monster_number.
*--------------------------------------------------------------------*
* Listing 2.68 FOR ALL ENTRIES on an Internal Table
*--------------------------------------------------------------------*
DATA(pets_of_our_monsters) =
FILTER #( monster_pets IN all_monsters
WHERE owner = monster_number ).