ABAPのFOR ALL ENTRIESとは?空テーブル・重複とJOINの使い分け

ABAPで内部テーブルの値を条件にデータをまとめて取得したいとき、FOR ALL ENTRIESは便利です。ただし、駆動する内部テーブルが空だと想定外の大量取得につながり、重複の扱いを誤ると必要な明細が消えたように見えることがあります。この記事では、空テーブルの防止、2種類の重複、INNER JOINとの使い分けを実装例で整理します。

FOR ALL ENTRIESの基本動作

FOR ALL ENTRIES IN @lt_keysは、内部テーブルの各行を検索条件として使い、得られた行を一つの結果にまとめます。たとえば、前処理で得た受注番号の一覧から、受注明細テーブルVBAPを読む場合に使えます。結果に同一行が複数あれば、DISTINCTを指定したときのように重複が除かれます。

落とし穴1:駆動テーブルが空ならSELECTを実行しない

駆動テーブルが空の場合、FOR ALL ENTRIESを使った検索条件は期待どおりに働きません。SAPの説明ではWHERE条件全体が無視され、データベースの全行が結果に入る可能性があります。「キーが0件だから結果も0件」と考えて無条件に実行するのは危険です。

TYPES: BEGIN OF ty_key,
         vbeln TYPE vbap-vbeln,
       END OF ty_key,
       BEGIN OF ty_item,
         vbeln TYPE vbap-vbeln,
         posnr TYPE vbap-posnr,
         matnr TYPE vbap-matnr,
       END OF ty_item.

DATA lt_keys  TYPE STANDARD TABLE OF ty_key WITH EMPTY KEY.
DATA lt_items TYPE STANDARD TABLE OF ty_item WITH EMPTY KEY.

" lt_keys は前処理で作成済み
SORT lt_keys BY vbeln.
DELETE ADJACENT DUPLICATES FROM lt_keys COMPARING vbeln.

CLEAR lt_items.
IF lt_keys IS NOT INITIAL.
  SELECT vbeln, posnr, matnr
    FROM vbap
    FOR ALL ENTRIES IN @lt_keys
    WHERE vbeln = @lt_keys-vbeln
    INTO TABLE @lt_items.
ENDIF.

ポイントは、IF lt_keys IS NOT INITIALでSELECTを囲むことです。取得先のlt_itemsを再利用する処理なら、実行前に空にしておくと、キー0件のときに前回の結果が残る事故も防げます。例は比較的新しいABAPの@記法を使っています。古いリリースでは、利用可能な構文に合わせて書き換えてください。

落とし穴2:「重複」には2種類ある

駆動テーブル内のキー重複

同じ受注番号がlt_keysに何度あっても、それだけを理由に結果行が増えるわけではありません。ただし、同じ条件を繰り返し渡す無駄が生じ得ます。上の例ではSORTとDELETE ADJACENT DUPLICATESで重複を除きました。元の内部テーブルが後続処理に必要なら、元データを直接削除せず、検索キーだけの別テーブルを作ります。ユニークキー付きのソートテーブルも有効です。

検索結果の行重複

FOR ALL ENTRIESの結果では、選択した列の値がすべて同じ行は一つにまとめられます。たとえば同じ受注の2明細に同じ品目があるとき、VBAPからVBELNとMATNRだけを選ぶと、2明細を区別できません。明細単位の件数や後続処理が必要なら、明細番号POSNRも選択します。駆動キーの重複除去と、結果行の重複除去は別の話です。

FOR ALL ENTRIESとINNER JOINの使い分け

両者は書き方だけでなく、入力と結果の意味が違います。INNER JOINはデータベース上の複数の表を結び、両方の表の項目を一度に取得できます。一方、FOR ALL ENTRIESはABAP側にすでにある内部テーブルの値で、次のSELECTを絞り込む用途に向きます。

状況まず検討する方法理由
必要な条件・項目がDB上の表にあるJOIN表同士の結合と絞り込みを一つのSELECTで表現できる
前処理や計算で作ったキーが内部テーブルにあるFOR ALL ENTRIES作成済みのキー一覧を次の取得条件に使える
結合先が1対多で明細行数を保ちたいJOINを含め結果の粒度を設計結合で行が増える条件と、取得するキー項目を明示できる
キーが大量、または性能が問題両案を実測件数、索引、バッファ、DB、実行計画で優劣が変わる

たとえば受注ヘッダVBAKの伝票タイプで絞り、その明細VBAPを取得するだけなら、最初からJOINで書くと分かりやすくなります。

DATA lv_auart TYPE vbak-auart VALUE 'OR'.

SELECT h~vbeln, i~posnr, i~matnr
  FROM vbak AS h
  INNER JOIN vbap AS i
    ON i~vbeln = h~vbeln
  WHERE h~auart = @lv_auart
  INTO TABLE @DATA(lt_items_by_join).

反対に、受注番号が画面入力の検証や複数の処理を経て内部テーブルにまとまっている場合は、先ほどのFOR ALL ENTRIESが自然です。JOINが常に速い、またはFOR ALL ENTRIESが常に速いとは決めず、対象件数を変えてSQLトレース(ST05)や実行時間で確認します。特に大量のキーを渡す場合は、DBに発行されるSQLの数や取得行数も見ます。

実装・レビュー時のチェックリスト

  • キー0件のときにSELECTを実行しないか
  • 取得先テーブルに前回の結果が残らないか
  • 駆動テーブルの重複キーを必要に応じて減らしているか
  • 結果に必要な明細キーまでSELECTしているか
  • JOINへ変えた場合、1対多の結合による行数の違いを理解しているか
  • 0件・1件・重複キー・大量件数で結果と性能を確認したか

まず結果の正しさを確認し、性能は実データに近い条件で測るのが確実です。空テーブルを防ぐガードと必要なキー列の選択を、コードレビューの定番項目にするとよいでしょう。

参考・関連記事

コメント

この記事へのトラックバックはありません。

PAGE TOP