ABAP 内部テーブル(Internal Table)のパフォーマンス検証:大量データをBINARY SEARCHで検索

  アドオンプログラム

概要

前回READ TABLEのBINARY SEARCHを使用したパフォーマンス検証を行ってみたが、実用性に乏しいサンプルコードであった為、実際に使えそうなソースコードを用い、且つ、大量のデータで検証を実施した。
データはBKPF(約50万件)、BSEG(約110万件)を使用している。

サンプルコード:通常のLoop文

DATA: it_bkpf TYPE TABLE OF bkpf,
      wa_bkpf TYPE bkpf,
      it_bseg TYPE TABLE OF bseg,
      wa_bseg TYPE bseg.

SELECT * INTO TABLE it_bkpf FROM bkpf.
SELECT * INTO TABLE it_bseg FROM bseg.

LOOP AT it_bkpf INTO wa_bkpf.
  LOOP AT it_bseg INTO wa_bseg 
    WHERE bukrs = wa_bkpf-bukrs 
      AND belnr = wa_bkpf-belnr 
      AND gjahr = wa_bkpf-gjahr.
  ENDLOOP.
ENDLOOP.

処理としては、単純にBKPFとBSEGを全件取得し、BKPFを元にBSEGを参照するというプログラムだ。WHERE句が内部的にどのような検索を行っているのは不明だが、おそらく110万件の検索を50万回行っていると想定している。プログラムはとてもシンプルであるが、60分以上プログラムが終了しないロジックとなってしまう。

次に上記をBINARY SEARCHを使い、且つ、最低限の検索回数となるよう変えてみよう。

サンプルコード:BINARY SEARCH + IF文

DATA: it_bkpf TYPE TABLE OF bkpf,
      wa_bkpf TYPE bkpf,
      it_bseg TYPE TABLE OF bseg,
      wa_bseg TYPE bseg,
      v_tabix TYPE sy-tabix.

SELECT * INTO TABLE it_bkpf FROM bkpf.
SELECT * INTO TABLE it_bseg FROM bseg.

SORT it_bkpf BY bukrs belnr gjahr.
SORT it_bseg BY bukrs belnr gjahr.

LOOP AT it_bkpf INTO wa_bkpf.
  
  READ TABLE it_bseg TRANSPORTING NO FIELDS
    WITH KEY bukrs = wa_bkpf-bukrs
             belnr = wa_bkpf-belnr
             gjahr = wa_bkpf-gjahr BINARY SEARCH.
  v_tabix = sy-tabix.

  LOOP AT it_bseg INTO wa_bseg FROM v_tabix.

    IF    wa_bseg-bukrs = wa_bkpf-bukrs
      AND wa_bseg-belnr = wa_bkpf-belnr
      AND wa_bseg-gjahr = wa_bkpf-gjahr.
    ELSE.
      EXIT.
    ENDIF.

  ENDLOOP.
ENDLOOP.

赤字が最初のプログラムソースからの変更点となっている。
まず最初にBINARY SEARCHを使う上では必ずSORTをしておこう。テーブル上の主キーだけで検索している為、上記例ではおそらく問題ないと思うが、ショートダンプ発生の原因になりえるので、BINARY SEARCHとSORTは必ずセットで書く事をお勧めする。
次にREAD TABLEでBINARY SEARCHを行っているが、この段階で内部テーブルの値は必要ない為、TRANSPORTING NO FIELDSオプションを使い少しでもパフォーマンスを減らしている。
LOOPでは内部テーブルの検索開始位置をBINARY SEARCHを使って見つけた行のみを指定している。ポイントはWHERE句に条件を書かず、IF文でチェックしている点だ。
この例では50万件もの検索に2分もかからず検索できた。
最後にIF文で記述した条件をWHERE句にしたらどうなるのかという事を検証してみた。

サンプルコード:BINARY SEARCH + WHERE句

DATA: it_bkpf TYPE TABLE OF bkpf,
      wa_bkpf TYPE bkpf,
      it_bseg TYPE TABLE OF bseg,
      wa_bseg TYPE bseg,
      v_tabix TYPE sy-tabix.

SELECT * INTO TABLE it_bkpf FROM bkpf.
SELECT * INTO TABLE it_bseg FROM bseg.

SORT it_bkpf BY bukrs belnr gjahr.
SORT it_bseg BY bukrs belnr gjahr.

LOOP AT it_bkpf INTO wa_bkpf.
  
  READ TABLE it_bseg TRANSPORTING NO FIELDS
    WITH KEY bukrs = wa_bkpf-bukrs
             belnr = wa_bkpf-belnr
             gjahr = wa_bkpf-gjahr BINARY SEARCH.
  v_tabix = sy-tabix.

  LOOP AT it_bseg INTO wa_bseg FROM v_tabix
   WHERE bukrs = wa_bkpf-bukrs
      AND belnr = wa_bkpf-belnr
      AND gjahr = wa_bkpf-gjahr.

  ENDLOOP.
ENDLOOP.

実行結果

対象ケース実行結果(分)
通常のLoop文2650分
BINARY SEARCH + IF文2分
BINARY SEARCH + WHERE句1470分

上記のように、想像以上の差異が発生した。
通常のLOOP文は想定通りであるが、WHERE句で指定した場合もパフォーマンスとしてはそれほど良くなかった。おそらく、インデックスの指定によって、検索対照は徐々に減っていくが、初期の検索がボトルネックになっているのだろう。
例えば、BPKFの1件目ではBSEGは150万回検索されている。BKPFの40万件目ではBSEGは30万回検索されるという風になっているのであろう。FROMだけ指定したとしてもWHERE句で条件をするだけではそれほど意味がない事が理解できる。IF文で指定した場合はBKPF1件に対してBSEGの検索はたった3回しかされない事になる。その為、これほどのパフォーマンスの差が発生したのだろう。
※BKPF1件に対して、BSEG3件という前提で記述しました。

パフォーマンスの検証としては2度程(内部テーブルのパフォーマンス向上:BINARY SEARCH有り無しの違いLOOP文の高速化(失敗))記事に掲載したが、BINARY SEARCHと IF文を組み合わせるのが最も早くできるのではないだろうか。引き続き様々な方向で検討していきたい。