内部テーブルのパフォーマンスの改善

ソートテーブルおよびハッシュテーブルの使用

Objective

After completing this lesson, you will be able to ソートテーブルとハッシュテーブルを使用します。

試してみる: コード例の準備

  1. Ctrl + Shift + N を選択して、新しい ABAP リポジトリオブジェクトを作成します。
  2. フィルタ項目に単語 database を入力し、検索結果 Database Table をマークして、Continue を選択します。
  3. 作業しているパッケージに新しいオブジェクトを割り当て、名称 ZS4D401_FLIGHTS を入力します。テーブルの説明を入力し、Next を選択します。
  4. オブジェクトを移送依頼に割り当て、Finish を選択します。
  5. 中括弧の間に以下のコードを入力します。
    Code Snippet
    1234567891011
    key client : abap.clnt not null; key carrier_id : /dmo/carrier_id not null; key connection_id : /dmo/connection_id not null; key flight_date : /dmo/flight_date not null; @Semantics.amount.currencyCode : 'zs4d401_flights.currency_code' price : /dmo/flight_price; currency_code : /dmo/currency_code; plane_type_id : /dmo/plane_type_id; seats_max : /dmo/plane_seats_max; seats_occupied : /dmo/plane_seats_occupied;
  6. Ctrl + F3 キーを押してテーブルを有効化します。
  7. ZCL_FILL_ZS4D401_FLIGHTS という名称の新規 ABAP クラスを登録します。
  8. main メソッドの実装に以下のコードを入力します。
    Code Snippet
    123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
    DATA flights TYPE TABLE OF /dmo/flight. DATA insert_tab TYPE TABLE OF /dmo/flight. DELETE FROM ZS4D401_flights. SELECT FROM /dmo/flight FIELDS * ORDER BY carrier_Id, connection_id INTO TABLE @flights . LOOP AT flights INTO DATA(first_date). * Original table flights has 2 flights per connection. Only process the first IF sy-tabix MOD 2 = 0. CONTINUE. ENDIF. *Extend flight dates by 2000 days DO 2000 TIMES. APPEND first_date TO insert_tab. first_date-flight_date += 1. ENDDO. * ENDIF. ENDLOOP. * Read highest connection number for each flight SELECT FROM /dmo/flight AS main FIELDS carrier_Id, connection_id, flight_date, price, currency_code, plane_type_id WHERE connection_id = ( SELECT MAX( connection_id ) FROM /dmo/flight WHERE carrier_id = main~carrier_id ) AND flight_Date = ( SELECT MIN( flight_date ) FROM /dmo/flight WHERE carrier_id = main~carrier_id AND connection_id = main~connection_id ) GROUP BY carrier_id, connection_Id, flight_date, price, currency_code, plane_type_id ORDER BY carrier_id, connection_id INTO TABLE @DATA(max). *Add 50 new connection numbers and 2000 days of flights for each LOOP AT max INTO DATA(line). DO 50 TIMES. line-connection_id += 1. line-plane_type_id = SWITCH #( CONV i( line-connection_id ) MOD 2 WHEN 0 THEN 'A330' WHEN 1 THEN 'A350' ). first_date = CORRESPONDING #( line ). DATA(repetitions) = COND i( WHEN line-carrier_id = 'LH' AND line-connection_id = '0405' THEN 4000 ELSE 2000 ). DO repetitions TIMES. first_date-seats_max = 220. APPEND first_date TO insert_tab. first_date-flight_date += 1. ENDDO. ENDDO. ENDLOOP. SORT insert_tab BY carrier_Id connection_id flight_date. DELETE ADJACENT DUPLICATES FROM insert_tab COMPARING carrier_id connection_id flight_date. INSERT ZS4D401_flights FROM TABLE @insert_tab. out->write( |Generated { sy-dbcnt } rows in table ZS4D401_flights| ).
  9. Ctrl + F3 キーを押して、クラスを有効化します。
  10. F9 を選択して、クラスを実行します。main メソッドは、次の 2 つのセクションのコードスニペットに必要な追加のデータベーステーブルに入力します。

ソート済およびハッシュ済テーブル

これまで、標準内部テーブルを使用していました。標準テーブルのデータは、特定のソート順序で保持されません。したがって、テーブルからデータを読み込む場合、必要な行が見つかるまで、テーブルを順次検索する必要があります。テーブルのサイズによっては、この処理に時間がかかる場合があります。

ソートテーブルまたはハッシュテーブルを使用した内部テーブルへの読込アクセスのパフォーマンスを最適化することができます。

ソートテーブルでは、テーブルの内容は常にキー項目に従って昇順でソートされます。テーブルに新しいレコードを挿入する場合は、システムによって正しい位置に配置されます。データは常にソートされているため、標準テーブルよりも効率的にレコードを取得することができます (特定のルールに従っている場合)。

ハッシュテーブルは、特別なハッシュアルゴリズムを使用して管理されます。これにより、テーブルが非常に大きな場合でもデータを迅速に取得することができます。ただし、このパフォーマンスの向上は、非常に特殊なケースでのみ機能します。

ソートテーブルへのアクセス

このビデオでは、ソートテーブルにアクセスする方法を学習します。

ハッシュテーブルを宣言するときは、ユニークキーを使用して定義してください。重複は許可されません。

ハッシュテーブルにアクセスする方法については、ビデオを視聴してください。

標準テーブル、ソートテーブル、ハッシュテーブルを使用する場合

内部テーブルにデータを書き込んでから、テーブル全体にループを実装する場合は、標準テーブルを使用することができます。ソートテーブルとハッシュテーブルのパフォーマンスの最適化は、ループにフィルタがないため関係ありません。

この例は、ソートテーブルとハッシュテーブルをいつ使用する必要があるかを示しています。これには、where 句の最初のキー項目を使用したテーブル接続に対するループが含まれます。これは、キーが完全には指定されていない場合でもバイナリ検索手法を使用できるため、ソートテーブルを使用する場合に該当します。

ループ内で、航空会社テーブルのエントリが検索されます。そのため、フルキーを使用してテーブルへのアクセスが繰り返されています。これは、ハッシュテーブルを使用する場合に最適です。

標準テーブル、ソートテーブル、およびハッシュテーブルの比較実行時間

この図は、標準テーブル、ソートテーブル、およびハッシュテーブルの実行時間を比較したものです。

ソートテーブルおよびハッシュテーブルの書込

標準テーブルを使用する場合は、APPEND 命令を使用して、作業領域の内容をテーブルの最後に追加することができます。これは、ハッシュテーブルでは実行できず、構文エラーが発生します。

技術的には、APPEND 命令を使用してソートテーブルにデータを書き込むことができます。ただし、追加しようとしている行がソート順序に従ってテーブルの最後に属していない場合は、実行時エラーが発生するリスクがあります。これは、この例では発生します。

ハッシュテーブルにデータを書き込み、ソートテーブルを安全に埋める方法は、INSERT 文を使用することです。INSERT は、新しいローがテーブル内の正しい位置に挿入されるようにします。

試してみる: ソート済およびハッシュ済テーブル

  1. インタフェース IF_OO_ADT_CLASSRUN を実装する新しいクラスを登録します。
  2. 以下のコードスニペットをメソッド if_oo_adt_classrun~main( ) の実装部分にコピーします。
    Code Snippet
    1234567891011
    * Run this class using the ABAP Profiler to measure relative access times for standard, sorted, and hashed tables data(flights) = new lcl_flights( ). flights->access_standard( ). flights->access_sorted( ). flights->access_hashed( ). out->write( |Done| ).
  3. Local Types タブに切り替えて、以下のコードスニペットをエディタにコピーします。
    Code Snippet
    1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
    CLASS lcl_flights DEFINITION. PUBLIC SECTION. METHODS constructor. METHODS access_standard. METHODS access_sorted. METHODS access_hashed. PRIVATE SECTION. DATA standard_table TYPE STANDARD TABLE OF zs4d401_flights WITH NON-UNIQUE KEY carrier_id connection_id flight_date. DATA sorted_table TYPE SORTED TABLE OF zs4d401_flights WITH NON-UNIQUE KEY carrier_id connection_id flight_date. DATA hashed_table TYPE HASHED TABLE OF zs4d401_flights WITH UNIQUE KEY carrier_id connection_id flight_date. DATA key_carrier_id TYPE /dmo/carrier_id. DATA key_connection_id TYPE /dmo/connection_id. DATA key_date TYPE /dmo/flight_date. METHODS set_line_to_read. ENDCLASS. CLASS lcl_flights IMPLEMENTATION. METHOD access_hashed. DATA(result) = hashed_table[ carrier_Id = me->key_carrier_id connection_Id = me->key_connection_id flight_date = me->key_date ]. ENDMETHOD. METHOD access_sorted. DATA(result) = sorted_table[ carrier_Id = me->key_carrier_id connection_Id = me->key_connection_id flight_date = me->key_date ]. ENDMETHOD. METHOD constructor. SELECT FROM zs4d401_flights FIELDS * INTO TABLE @standard_table. SELECT FROM zs4d401_flights FIELDS * INTO TABLE @sorted_table. SELECT FROM zs4d401_flights FIELDS * INTO TABLE @hashed_table. set_line_to_read( ). ENDMETHOD. METHOD access_standard. DATA(result) = standard_table[ carrier_Id = me->key_carrier_id connection_Id = me->key_connection_id flight_date = me->key_date ]. ENDMETHOD. METHOD set_line_to_read. DATA(line) = standard_table[ CONV i( lines( standard_table ) * '0.65' ) ]. me->key_carrier_id = line-carrier_Id. me->key_connection_Id = line-connection_id. me->key_date = line-flight_date. ENDMETHOD. ENDCLASS.
  4. Ctrl + F3 キーを押して、クラスを有効化します。
  5. エディタを右クリックし、Profile As... を選択します。ABAP アプリケーション (コンソール)
  6. Details で、チェックボックス SQL database access のチェックを外し、Finish を選択します。
  7. ABAP プロファイラパースペクティブに切り替え、トレース項目をダブルクリックします (最初に表示をリフレッシュする必要がある場合があります)。
  8. 該当対象一覧を選択します。
  9. メソッド access_standard( )access_sorted( )、および access_hashed( ) の実行時間を比較します。

ソートテーブルおよびハッシュテーブルの使用

コードに、テーブルタイプが標準テーブルの大きな内部テーブルが含まれていることに気付きました。これらのテーブルへのアクセス方法をコードで分析し、キーによってのみアクセスされていることを検出します。パフォーマンスを改善するには、内部テーブルの定義を、アクセス方法に応じてソートテーブルまたはハッシュテーブルのいずれかに変更します。

テンプレート:

  • /LRN/CL_S4D401_ITS_FSYM (グローバルクラス)

ソリューション:

  • /LRN/CL_S4D401_ITS_TABLE_KIND (グローバルクラス)

タスク 1: テンプレートのコピー (オプション)

テンプレートクラス /LRN/CL_S4D401_ITS_FSYM をコピーします。前の演習問題を完了した場合は、このタスクをスキップし、クラス ZCL_##_SOLUTION の編集を続行することができます。

ステップ

  1. クラス /LRN/CL_S4D401_ITS_FSYM を各自のパッケージのクラスにコピーします (推奨名: ZCL_##_SOLUTION、## はグループ番号)。

    1. Project Explorer で、クラス /LRN/CL_S4D401_ITS_FSYM を右クリックしてコンテキストメニューを開きます。

    2. コンテキストメニューから Duplicate.... を選択します。

    3. Package 項目にパッケージの名称を入力します。Name 項目に、ZCL_##_SOLUTION (## はグループ番号) と入力します。

    4. Next を選択します。

    5. 移送依頼を確認し、Finish を選択します。

  2. コピーを有効化します。

    1. Ctrl + F3 を押してクラスを有効化します。

タスク 2: CONNECTIONS_BUFFER の調整

ローカルクラス LCL_PASSENGER_FLIGHT の静的属性 connections_bufferの使用先一覧を使用します。connections_buffer の定義を変更して、アクセス方法が最適になるようにします。

ステップ

  1. ローカルクラス LCL_PASSENGER_FLIGHT の静的属性 connections_buffer の定義にナビゲートします。

    1. たとえば、Outline ビューで LCL_PASSENGER_FLIGHT ノードを展開し、CONNECTIONS_BUFFER を選択します

  2. connections_buffer 属性の使用先一覧を作成します。

    1. CLASS-DATA 命令で connections_buffer を右クリックし、使用先一覧取得を選択します。または、カーソルを connections_buffer に置いて Ctrl + Shift + G を押すこともできます。

    2. エディタの下の検索ビューで一覧を分析します。判定にカーソルを合わせると、文全体が表示されます。

  3. connections_buffer 属性の定義を調整します。読込アクセスに最も適したテーブルタイプとキー定義を選択します。

    1. テーブルの種類を HASHED TABLE に変更します。

    2. キー定義を、carrier_id および connection_id をキー項目とする一意のキーに変更します。

    3. コードを以下のように調整します。

      ABAP
      1234
      * CLASS-DATA connections_buffer TYPE TABLE OF st_connections_buffer. CLASS-DATA connections_buffer TYPE HASHED TABLE OF st_connections_buffer WITH UNIQUE KEY carrier_id connection_id.

タスク 3: FLIGHTS_BUFFER の調整

ローカルクラス LCL_PASSENGER_FLIGHT の静的属性 flights_bufferの使用先一覧を使用します。flights_buffer の定義を変更して、属性へのアクセス方法が最適になるようにします。

ステップ

  1. ローカルクラス LCL_PASSENGER_FLIGHT の静的属性 flights_buffer の定義にナビゲートします。

    1. たとえば、アウトラインビューでノード LCL_PASSENGER_FLIGHT を展開し、FLIGHTS_BUFFER を選択します。

  2. flights_buffer 属性の使用先一覧を登録します。

    1. CLASS-DATA 命令で flights_buffer を右クリックし、使用先一覧取得を選択します。または、カーソルを flights_buffer に置いて Ctrl + Shift + G を押すこともできます。

    2. エディタの下の検索ビューで一覧を分析します。判定にカーソルを合わせると、文全体が表示されます。

  3. 属性 flights_buffer の定義を調整します。読込アクセスに最も適したテーブルタイプとキー定義を選択します。

    1. テーブルの種類を SORTED TABLE に変更します。

    2. キー定義を、carrier_idconnection_id、および flight_date をキー項目とする非一意キーに変更します。

    3. コードを以下のように調整します。

      ABAP
      1234
      * CLASS-DATA: flights_buffer TYPE TABLE OF st_flights_buffer. CLASS-DATA: flights_buffer TYPE SORTED TABLE OF st_flights_buffer WITH NON-UNIQUE KEY carrier_id connection_id flight_date.
  4. Problems ビューで構文エラーを分析します。

    1. エディタの下にある Problems ビューを分析します。

  5. 冗長な SORT 命令を削除またはコメントします。

    1. コードを以下のように調整します。

      ABAP
      1
      * SORT flights_buffer BY carrier_id connection_id flight_date.
  6. グローバルクラスをコンソールアプリとして有効化してテストします。

    1. Ctrl + F3 を押します。

    2. F9 を押します。

タスク 4: 改善の定量化

ABAP プロファイリングを使用して、2 つの内部テーブルのタイプを調整することで実現されるパフォーマンス改善を定量化します。

ステップ

  1. Project Explorer でテンプレートクラス /LRN/CL_S4D401_ITS_FSYM を見つけます。

  2. テンプレートクラス /LRN/CL_S4D401_ITS_FSYM を ABAP コンソールアプリとしてプロファイルします。

    1. Project Explorer でテンプレートクラス /LRN/CL_S4D401_ITS_FSYM を右クリックし、Profile AsABAP Application (Console) を選択します。

    2. Finish を選択します。

    3. コンソールビューが表示されるか、更新されるまで待機します。

  3. 独自のクラス (ZCL_##_SOLUTION、## は各自のグループ番号) に戻り、同じ方法でプロファイルします。

    1. Project Explorer で独自のクラスを見つけます。

    2. Project Explorer でクラスを右クリックし、Profile AsABAP Application (Console) を選択します。

    3. コンソールビューが表示されるか、更新されるまで待機します。

  4. ABAP Profiling パースペクティブに切り替え、2 つの ABAP トレースを比較します。

    1. ABAP Profiling パースペクティブを開くには、WindowPerspectiveOpen PerspectiveOther.... を選択します。eclipse メニューから選択します。次に、ABAP Profiling and Open を選択します。(または、可能な場合は、Eclipse ツールバーの右上隅にある ABAP Profiling アイコンを選択することもできます。)

    2. 下部の ABAP トレースビューで、ABAP クラウドプロジェクトに対応するノードを展開します。

    3. F5 を押して、トレース結果の一覧をリフレッシュします。最初の 2 つのトレース結果の名称は、独自の ABAP クラスおよびテンプレートクラスと同様にする必要があります。

    4. 列実行時間の値を比較します。

  5. 分析後、ABAP パースペクティブに戻ります。

    1. 以前と同様にパースペクティブを切り替えます。