ABAP New Features - Internal Tables

weixin_34253539發表於2017-07-27

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(<monster_details>).

    LOOP AT table_of_monsters ASSIGNING FIELD-SYMBOL(<monster_details2>).
    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:
      <green_monsters> LIKE LINE OF green_monsters,
      <blue_monsters>  LIKE LINE OF blue_monsters.

    LOOP AT green_monsters ASSIGNING <green_monsters>.
      APPEND INITIAL LINE TO blue_monsters
      ASSIGNING <blue_monsters>.
      MOVE-CORRESPONDING <green_monsters> TO <blue_monsters>.
      CLEAR <blue_monsters>-evilness.
      <blue_monsters>-people_scared =
      <green_monsters>-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(<european_result>).
      APPEND INITIAL LINE TO us_results ASSIGNING FIELD-SYMBOL(<us_result>).
      MOVE-CORRESPONDING <european_result> TO <us_result>.
    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 table2MOVE -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(<monster_details2>).
    ENDLOOP.

判斷是否存在也可以用line_exists來替代。

*--------------------------------------------------------------------*
* Listing 2.67 Seeing Whether an Internal Table Line Exists before 7.4
*--------------------------------------------------------------------*
    READ TABLE table_of_monsters ASSIGNING <monster_details>
    WITH KEY monster_number = monster_number.

    IF sy-subrc NE 0.
      APPEND INITIAL LINE TO table_of_monsters ASSIGNING <monster_details>.
    ENDIF.
    ADD 1 TO <monster_details>-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 <monster_details>
      WITH KEY monster_number = monster_number.
    ELSE.
      APPEND INITIAL LINE TO table_of_monsters ASSIGNING <monster_details>.
    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 <monster_group_record> ASSIGNING FIELD-SYMBOL(<bonkers_monsters>)來取出該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(<monster_group_record>).

      CHECK <monster_group_record>-is_it_crackers = abap_true.

      CLEAR monster_sub_set.

      LOOP AT GROUP <monster_group_record> ASSIGNING FIELD-SYMBOL(<bonkers_monsters>).
        monster_sub_set = VALUE #( BASE monster_sub_set ( <bonkers_monsters> ) ).
      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_group_record>-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 ).

相關文章