ABAP New open SQL:Code Pushdown って何のこと?

Code Pushdown ってなんのことだろう?

2023-08-13:続編があります。こっちのほうがより実践的かもしれない。
ABAP New open SQL:Code Pushdown って何のこと?(2) - コントロールを取り戻せ!
ABAP New open SQL:Code Pushdown って何のこと?(3) - コントロールを取り戻せ!
2023-08-13:比較記事を書きました。(ABAP)CDS View vs HANA(Information)View
ABAP CDS View vs HANA Information View(HANA View):メモリ使用量比較|基幹システムのクラウド移行・構築・導入支援のBeeX

  • 先日、SAP HANAの勉強会に参加したときのこと。
  • 多少時代遅れ感はありますが、「ABAP CDS View vs HANA Infomation View」のスライドがありまして、ざっくり体感的には、HANA Infomation Viewが3倍から30倍くらい高速に動作します。
  • 一時期、営業的なのかどうか不明ですが、SAPJ様が「ABAP CDS Viewのほうが速い」と発信していたらしいですが、実測してみると、HANA Infomation Viewの圧勝で、SAP HANAの仕組みを少し考えると、当然ということに気づきます。
  • ただし、1千万~10億レコードといったボリュームの大きなデータを扱うときの話で、プロジェクトでアドオンプログラム(10万レコード程度)のパフォーマンスが出なくて困っていることとリンクしていないことが、なかなか伝わらないようでした。
  • アドオンプログラムの場合、Netweaverのメモリ容量制限から、扱えるのは500万レコード位でしょうか?桁が違う話なのです。
  • パフォーマンスが正義みたいな考え方はあまり好きでは無いのですが、R/3時代のルールで作成した(ITAB多用)プログラムも含めて乱暴な比較をすると以下のような感じになると思います。
    • 1:ITAB多用(BINARY SEARCH等をガチガチに組んだ場合)
    • 100:ABAP CDS View(パフォーマンス上は、Classic Open SQL、New Open SQLも同じ)
    • 1000:HANA Infomation View

(数値が大きい方が高速動作)

昔々SAP社様が、「東京→大阪を数秒で行ける」といった宣伝をしていました。
「ITAB多用で、2時間30分→9,000秒、,1,000倍高速なHANA Infomation Viewで置き換えに成功したとすると、9秒」
条件付きではありますが、嘘は言っていなかったと思います。
でも、これが90秒でもおそらく万々歳だと思うのです。そして、New Open SQLのみで実現できるとしたら...嬉しいですよね。

一方で営業的には、
「リアルタイム経営の為には、ビックデータの高速分析が必要です。Code Pushdownという設計手法があり、CDS Viewを採用することで実現します。」
CDS→Pushdown→リアルタイム経営」みたいな。

多少逆説的ですが、ABAPコードの中の「LOOP文を、廃止または削減」すると、Code Pushdownが実現できると思います。そして処理速度100倍も夢ではないと思います。っということでプログラムを作ってみました。
外見上の振る舞いは変えずに5種類の実装方法を作りました。
処理としては、トランザクションを格納したテーブルから、会社コードのバリエーション一覧を取得します。

A-1
 従来方式です。
 ACDOCAのデータを取得して
 SORT命令で並び替えて(Non-Pushdown)
 DELETE命令で重複を削除する(Non-Pushdown)

A-2
 従来方式の変形です。
 ACDOCAのデータをORDER BY句で並び替えて取得して(Pushdown)
 DELETE命令で重複を削除する(Non-Pushdown)

B-1
 ACDOCAのデータをDISTINCT句で重複削除して(Pushdown)
 ORDER BY句で並び替えて取得する(Pushdown)

B-2
 ACDOCAのデータをGROUP BY句で重複削除して(Pushdown)
 ORDER BY句で並び替えて取得する(Pushdown)

C-1
 番外編
 ACDOCAのデータを取得して
 LOOP + BINARY SEARCHで、重複削除および並び替えする(Non-Pushdown)

私の環境では以下の通りになりました。
ACDOCA:300万レコード→実行結果:20レコード
A-1:2.9sec
A-2:1.0sec
B-1:0.018sec
B-2:0.018sec
C-1:3.0sec
純粋なITAB(Non-Pushdown)のA-1とC-1と比較して、PushdownしているB-1、B-2は100倍以上の処理速度となりました。
そして、HANA Infomation Viewはこれより更に10倍高速動作する可能性があります。

ABAP CDS Viewは、ABAP層で動作しているので、Open SQL(New, Classic含む)とほぼ同じ速度です。だから、使い慣れたOpen SQLにちょい足しをして、もっとカジュアルにCode Pushdownすると良いのではと思います。

2023-08-14:ラジオボタンで選択できるように修正。バグ修正

*&---------------------------------------------------------------------*
*& Report SQL_Console
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT SQL_Console.

*-----------------------------------------------------------------------
* GLOBAL VARIABLE
*-----------------------------------------------------------------------
DATA:
  GDS_BAPIRET2 TYPE BAPIRET2,                     "リターンパラメータ
  GDF_SUBRC    TYPE SY-SUBRC.                     "リターンコード

*-----------------------------------------------------------------------
* CONDITION VIEW
*-----------------------------------------------------------------------

PARAMETERS P_ISDISP AS CHECKBOX TYPE UFVISIBLE DEFAULT ABAP_FALSE."表示フラグ
PARAMETERS P_TBMAXS TYPE TBMAXSEL DEFAULT 1000000."選択するエントリの最大数
PARAMETERS:
  P_740A1 RADIOBUTTON GROUP RB1,                 "A-1:ACDOCAのデータを取得して、SORT命令で並び替えて、DELETE命令で重複を削除する
  P_740A2 RADIOBUTTON GROUP RB1,                 "A-2:ACDOCAのデータをORDER BY句で並び替えて取得して、"DELETE命令で重複を削除する
  P_740B1 RADIOBUTTON GROUP RB1,                 "B-1:ACDOCAのデータをDISTINCT句で重複削除して、ORDER BY句で並び替えて取得する
  P_740B2 RADIOBUTTON GROUP RB1,                 "B-2:ACDOCAのデータをGROUP BY句で重複削除して、ORDER BY句で並び替えて取得する
  P_740C1 RADIOBUTTON GROUP RB1.                 "C-1:番外編、ACDOCAのデータを取得して、LOOP + BINARY SEARCHで、重複削除および並び替えする

*-----------------------------------------------------------------------
* START-OF-SELECTION
*-----------------------------------------------------------------------
START-OF-SELECTION.
  CASE ABAP_TRUE.
    WHEN P_740A1.
      PERFORM START_OF_SELECTION_740A1
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_740A2.
      PERFORM START_OF_SELECTION_740A2
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_740B1.
      PERFORM START_OF_SELECTION_740B1
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_740B2.
      PERFORM START_OF_SELECTION_740B2
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_740C1.
      PERFORM START_OF_SELECTION_740C1
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN OTHERS.
      LEAVE TO LIST-PROCESSING.
  ENDCASE.

*-----------------------------------------------------------------------
* SUBROUTINE
*-----------------------------------------------------------------------
FORM START_OF_SELECTION_740A1
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "ACDOCAに登録済みの会社コードを並び替えて一覧出力する
  SELECT
      ACDOCA~RBUKRS
    FROM ACDOCA
    INTO TABLE @DATA(LDT_DATA).
  SORT LDT_DATA ASCENDING BY
    RBUKRS.
  DELETE ADJACENT DUPLICATES FROM LDT_DATA COMPARING
    RBUKRS.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA(LDF_DATA_COUNT) = LINES( LDT_DATA ).
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = DATA(LDO_ALV)
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO DATA(LDX_SALV_MSG).
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  DATA(LDO_HEADER) = NEW CL_SALV_FORM_LAYOUT_GRID( ).
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'ACDOCA' ).
  SELECT FROM ACDOCA FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_740A2
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "ACDOCAに登録済みの会社コードを並び替えて一覧出力する
  SELECT
      ACDOCA~RBUKRS
    FROM ACDOCA
    ORDER BY
      ACDOCA~RBUKRS
    INTO TABLE @DATA(LDT_DATA).
  DELETE ADJACENT DUPLICATES FROM LDT_DATA COMPARING
    RBUKRS.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA(LDF_DATA_COUNT) = LINES( LDT_DATA ).
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = DATA(LDO_ALV)
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO DATA(LDX_SALV_MSG).
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  DATA(LDO_HEADER) = NEW CL_SALV_FORM_LAYOUT_GRID( ).
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'ACDOCA' ).
  SELECT FROM ACDOCA FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_740B1
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "ACDOCAに登録済みの会社コードを並び替えて一覧出力する
  SELECT DISTINCT
      ACDOCA~RBUKRS
    FROM ACDOCA
    ORDER BY
      ACDOCA~RBUKRS
    INTO TABLE @DATA(LDT_DATA).

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA(LDF_DATA_COUNT) = LINES( LDT_DATA ).
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = DATA(LDO_ALV)
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO DATA(LDX_SALV_MSG).
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  DATA(LDO_HEADER) = NEW CL_SALV_FORM_LAYOUT_GRID( ).
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'ACDOCA' ).
  SELECT FROM ACDOCA FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_740B2
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "ACDOCAに登録済みの会社コードを並び替えて一覧出力する
  SELECT
      ACDOCA~RBUKRS
    FROM ACDOCA
    GROUP BY
      ACDOCA~RBUKRS
    ORDER BY
      ACDOCA~RBUKRS
    INTO TABLE @DATA(LDT_DATA).

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA(LDF_DATA_COUNT) = LINES( LDT_DATA ).
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = DATA(LDO_ALV)
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO DATA(LDX_SALV_MSG).
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  DATA(LDO_HEADER) = NEW CL_SALV_FORM_LAYOUT_GRID( ).
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'ACDOCA' ).
  SELECT FROM ACDOCA FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_740C1
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "ACDOCAに登録済みの会社コードを並び替えて一覧出力する
  SELECT
      ACDOCA~RBUKRS
    FROM ACDOCA
    INTO TABLE @DATA(LDT_DATA_1).
  DATA LDT_DATA LIKE LDT_DATA_1.
  LOOP AT LDT_DATA_1 ASSIGNING FIELD-SYMBOL(<LFS_DATA_1>).
    READ TABLE LDT_DATA
        TRANSPORTING NO FIELDS
        WITH KEY RBUKRS = <LFS_DATA_1>-RBUKRS
        BINARY SEARCH.
    IF SY-SUBRC <> 0.
      INSERT <LFS_DATA_1>
          INTO LDT_DATA
          INDEX SY-TABIX.
      CONTINUE.
    ENDIF.
  ENDLOOP.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA(LDF_DATA_COUNT) = LINES( LDT_DATA ).
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = DATA(LDO_ALV)
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO DATA(LDX_SALV_MSG).
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  DATA(LDO_HEADER) = NEW CL_SALV_FORM_LAYOUT_GRID( ).
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'ACDOCA' ).
  SELECT FROM ACDOCA FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

参考までに、Non-SAP HANA(Oracle 10.2)で動作するSAP ERP 6.0で同様の検証をしてみました。
以下ソースコードは、ABAP 7.31で動作しています。そのため、Classic Open SQLで記述しています。
ACDOCAは無いので、BKPFで実装しました。

BKPF:32万レコード→実行結果:150レコード
A-1:0.21sec
A-2:0.15sec
B-1:0.10sec
B-2:0.11sec
C-1:0.22sec

データ量が少なかったのでドラスティックな差は出ませんでしたが、Non-PushdownのA-1、C-1が大体0.2sec、PushdownのB-1、B-2が大体0.1sec、半分ずつのA-2が結果も中間の0.15secとなりました。さすがにOracleも10.2くらい新しいと、Pushdownした方が良好な結果になりました。実際は、ITABを高速に扱うのは高度なスキルが必要と思います。過不足なくBINARY SEARCHを完璧に使えているプログラムはほとんどないと思います。だから実際はもっと差がつくと思います。

2023-08-14:ラジオボタンで選択できるように修正。

*&---------------------------------------------------------------------*
*& Report SQL_Console
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT SQL_Console.

*-----------------------------------------------------------------------
* GLOBAL VARIABLE
*-----------------------------------------------------------------------
DATA:
  GDS_BAPIRET2 TYPE BAPIRET2,                     "リターンパラメータ
  GDF_SUBRC    TYPE SY-SUBRC.                     "リターンコード

*-----------------------------------------------------------------------
* CONDITION VIEW
*-----------------------------------------------------------------------

PARAMETERS P_ISDISP AS CHECKBOX TYPE UFVISIBLE DEFAULT ABAP_FALSE."表示フラグ
PARAMETERS P_TBMAXS TYPE TBMAXSEL DEFAULT 1000000."選択するエントリの最大数
PARAMETERS:
  P_731A1 RADIOBUTTON GROUP RB1,                 "A-1:BKPFのデータを取得して、SORT命令で並び替えて、DELETE命令で重複を削除する
  P_731A2 RADIOBUTTON GROUP RB1,                 "A-2:BKPFのデータをORDER BY句で並び替えて取得して、"DELETE命令で重複を削除する
  P_731B1 RADIOBUTTON GROUP RB1,                 "B-1:BKPFのデータをDISTINCT句で重複削除して、ORDER BY句で並び替えて取得する
  P_731B2 RADIOBUTTON GROUP RB1,                 "B-2:BKPFのデータをGROUP BY句で重複削除して、ORDER BY句で並び替えて取得する
  P_731C1 RADIOBUTTON GROUP RB1.                 "C-1:番外編、BKPFのデータを取得して、LOOP + BINARY SEARCHで、重複削除および並び替えする

*-----------------------------------------------------------------------
* START-OF-SELECTION
*-----------------------------------------------------------------------
START-OF-SELECTION.
  CASE ABAP_TRUE.
    WHEN P_731A1.
      PERFORM START_OF_SELECTION_731A1
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_731A2.
      PERFORM START_OF_SELECTION_731A2
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_731B1.
      PERFORM START_OF_SELECTION_731B1
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_731B2.
      PERFORM START_OF_SELECTION_731B2
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_731C1.
      PERFORM START_OF_SELECTION_731C1
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN OTHERS.
      LEAVE TO LIST-PROCESSING.
  ENDCASE.

*-----------------------------------------------------------------------
* SUBROUTINE
*-----------------------------------------------------------------------
FORM START_OF_SELECTION_731A1
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始
  "BKPFに登録済みの会社コードを並び替えて一覧出力する

  TYPES:
    BEGIN OF LTS_DATA,
      BUKRS TYPE BSEG-BUKRS,
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA.

  SELECT
      BKPF~BUKRS
    INTO TABLE LDT_DATA
    FROM BKPF.
  SORT LDT_DATA ASCENDING BY
    BUKRS.
  DELETE ADJACENT DUPLICATES FROM LDT_DATA COMPARING
    BUKRS.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA LDF_DATA_COUNT TYPE INT4.
  DESCRIBE TABLE LDT_DATA LINES LDF_DATA_COUNT.
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  DATA:
    LDO_ALV      TYPE REF TO CL_SALV_TABLE,
    LDX_SALV_MSG TYPE REF TO CX_SALV_MSG.
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = LDO_ALV
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO LDX_SALV_MSG.
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  "ヘッダ表示
  DATA LDO_HEADER TYPE REF TO CL_SALV_FORM_LAYOUT_GRID.
  CREATE OBJECT LDO_HEADER TYPE CL_SALV_FORM_LAYOUT_GRID.
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'BKPF' ).
  DATA LDF_COUNT3 TYPE INT4.
  SELECT COUNT(*) FROM BKPF INTO LDF_COUNT3.
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_731A2
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始
  "BKPFに登録済みの会社コードを並び替えて一覧出力する

  TYPES:
    BEGIN OF LTS_DATA,
      BUKRS TYPE BSEG-BUKRS,
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA.

  SELECT
      BKPF~BUKRS
    INTO TABLE LDT_DATA
    FROM BKPF
    ORDER BY
      BKPF~BUKRS.
  DELETE ADJACENT DUPLICATES FROM LDT_DATA COMPARING
    BUKRS.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA LDF_DATA_COUNT TYPE INT4.
  DESCRIBE TABLE LDT_DATA LINES LDF_DATA_COUNT.
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  DATA:
    LDO_ALV      TYPE REF TO CL_SALV_TABLE,
    LDX_SALV_MSG TYPE REF TO CX_SALV_MSG.
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = LDO_ALV
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO LDX_SALV_MSG.
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  "ヘッダ表示
  DATA LDO_HEADER TYPE REF TO CL_SALV_FORM_LAYOUT_GRID.
  CREATE OBJECT LDO_HEADER TYPE CL_SALV_FORM_LAYOUT_GRID.
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'BKPF' ).
  DATA LDF_COUNT3 TYPE INT4.
  SELECT COUNT(*) FROM BKPF INTO LDF_COUNT3.
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_731B1
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始
  "BKPFに登録済みの会社コードを並び替えて一覧出力する

  TYPES:
    BEGIN OF LTS_DATA,
      BUKRS TYPE BSEG-BUKRS,
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA.

  SELECT DISTINCT
      BKPF~BUKRS
    INTO TABLE LDT_DATA
    FROM BKPF
    ORDER BY
      BKPF~BUKRS.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA LDF_DATA_COUNT TYPE INT4.
  DESCRIBE TABLE LDT_DATA LINES LDF_DATA_COUNT.
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  DATA:
    LDO_ALV      TYPE REF TO CL_SALV_TABLE,
    LDX_SALV_MSG TYPE REF TO CX_SALV_MSG.
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = LDO_ALV
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO LDX_SALV_MSG.
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  "ヘッダ表示
  DATA LDO_HEADER TYPE REF TO CL_SALV_FORM_LAYOUT_GRID.
  CREATE OBJECT LDO_HEADER TYPE CL_SALV_FORM_LAYOUT_GRID.
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'BKPF' ).
  DATA LDF_COUNT3 TYPE INT4.
  SELECT COUNT(*) FROM BKPF INTO LDF_COUNT3.
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_731B2
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始
  "BKPFに登録済みの会社コードを並び替えて一覧出力する

  TYPES:
    BEGIN OF LTS_DATA,
      BUKRS TYPE BSEG-BUKRS,
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA.

  SELECT
      BKPF~BUKRS
    INTO TABLE LDT_DATA
    FROM BKPF
    GROUP BY
      BKPF~BUKRS
    ORDER BY
      BKPF~BUKRS.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA LDF_DATA_COUNT TYPE INT4.
  DESCRIBE TABLE LDT_DATA LINES LDF_DATA_COUNT.
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  DATA:
    LDO_ALV      TYPE REF TO CL_SALV_TABLE,
    LDX_SALV_MSG TYPE REF TO CX_SALV_MSG.
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = LDO_ALV
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO LDX_SALV_MSG.
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  "ヘッダ表示
  DATA LDO_HEADER TYPE REF TO CL_SALV_FORM_LAYOUT_GRID.
  CREATE OBJECT LDO_HEADER TYPE CL_SALV_FORM_LAYOUT_GRID.
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'BKPF' ).
  DATA LDF_COUNT3 TYPE INT4.
  SELECT COUNT(*) FROM BKPF INTO LDF_COUNT3.
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

FORM START_OF_SELECTION_731C1
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
    CHANGING
      POS_BAPIRET2 TYPE BAPIRET2
      POF_SUBRC TYPE SY-SUBRC.
*-----------------------------------------------------------------------
  CLEAR:
      POS_BAPIRET2,                               "リターンパラメータ
      POF_SUBRC.                                  "リターンコード
* Please write the SQL statement here ☆
* Set the internal table to be stored to LDT_DATA ☆
* Inline definition of LDT_DATA is fun ☆
  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始
  "BKPFに登録済みの会社コードを並び替えて一覧出力する

  TYPES:
    BEGIN OF LTS_DATA,
      BUKRS TYPE BSEG-BUKRS,
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA.

  DATA LDT_DATA_1 LIKE LDT_DATA.
  FIELD-SYMBOLS <LFS_DATA_1> LIKE LINE OF LDT_DATA_1.
  SELECT
      BKPF~BUKRS
    INTO TABLE LDT_DATA_1
    FROM BKPF.
  LOOP AT LDT_DATA_1 ASSIGNING <LFS_DATA_1>.
    READ TABLE LDT_DATA
        TRANSPORTING NO FIELDS
        WITH KEY BUKRS = <LFS_DATA_1>-BUKRS
        BINARY SEARCH.
    IF SY-SUBRC <> 0.
      INSERT <LFS_DATA_1>
          INTO LDT_DATA
          INDEX SY-TABIX.
      CONTINUE.
    ENDIF.
  ENDLOOP.

  DATA LDF_TIMESTAMP_END TYPE TIMESTAMPL.         "タイムスタンプデータ取得終了
  GET TIME STAMP FIELD LDF_TIMESTAMP_END.         "タイムスタンプデータ取得終了
  DATA LDF_DATA_COUNT TYPE INT4.
  DESCRIBE TABLE LDT_DATA LINES LDF_DATA_COUNT.
  IF PIF_ISDISP = ABAP_FALSE.
    CLEAR LDT_DATA.
  ENDIF.
*-----------------------------------------------------------------------
  DATA:
    LDO_ALV      TYPE REF TO CL_SALV_TABLE,
    LDX_SALV_MSG TYPE REF TO CX_SALV_MSG.
  TRY.
      CL_SALV_TABLE=>FACTORY(
        IMPORTING
          R_SALV_TABLE = LDO_ALV
        CHANGING
          T_TABLE      = LDT_DATA ).
    CATCH CX_SALV_MSG INTO LDX_SALV_MSG.
      POF_SUBRC = 8.
      RETURN.
  ENDTRY.

  "ヘッダ表示
  DATA LDO_HEADER TYPE REF TO CL_SALV_FORM_LAYOUT_GRID.
  CREATE OBJECT LDO_HEADER TYPE CL_SALV_FORM_LAYOUT_GRID.
  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 TEXT = 'Performance data' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 1 TEXT = 'Input records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 2 TEXT = 'BKPF' ).
  DATA LDF_COUNT3 TYPE INT4.
  SELECT COUNT(*) FROM BKPF INTO LDF_COUNT3.
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 1 TEXT = 'Output records' ).
  LDO_HEADER->CREATE_TEXT( ROW = 3 COLUMN = 2 TEXT = LDF_DATA_COUNT ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 1 TEXT = 'Responce Time [sec]' ).
  LDO_HEADER->CREATE_TEXT( ROW = 4 COLUMN = 2 TEXT = CL_ABAP_TSTMP=>SUBTRACT( TSTMP1 = LDF_TIMESTAMP_END TSTMP2 = LDF_TIMESTAMP_START ) ).

  LDO_ALV->SET_TOP_OF_LIST( LDO_HEADER ).
  LDO_ALV->SET_TOP_OF_LIST_PRINT( LDO_HEADER ).

  LDO_ALV->GET_FUNCTIONS( )->SET_ALL( ).
  LDO_ALV->GET_COLUMNS( )->SET_OPTIMIZE( ).
  LDO_ALV->GET_DISPLAY_SETTINGS( )->SET_STRIPED_PATTERN( ABAP_TRUE ).
  LDO_ALV->GET_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.

当初は、DISTINCTとGROUP BYのどちらが速いか?をテーマにしようと思っていたのですが、急遽変更しました。

5年くらい前はGROUP BYの圧勝だったんだけれど、今回なんかねぇ~あまり差がつかなかったんだよね~。

以上

検索キーワード
ABAP
ABAP 7.31
ABAP 7.4
ABAP 7.50
ABAP 7.51
ABAP 7.52
SQL
Open SQL
New Open SQL
Classic Open SQL
ABAP SQL
SAP HANA
Code to Data
Code Pushdown
SAP ERP 6.0
SAP S/4HANA

最近のヘビロテです。
www.youtube.com