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

  • ABAP New open SQL:Code Pushdown って何のこと?(3)
  • お決まりの枕詞ですみません。ソースコード著作権の類を放棄したわけではありません。勝手に使ったら怒ります。(爆)連絡してください。パフォーマンスデータを集めたいのです。手伝ってください。
  • さて長いブランクの後でようやく第三弾!!しかしながら、第二弾と同じ購買発注明細一覧になってしまいました。私にとっての初のABAPキャリアは、Web Dynpro for ABAPによる、MM領域だったのでEKPOが好きなんです。(そんなのどうでもいい情報ですね)
  • 購買発注明細一覧です。前回は基本的には単純なテーブルJOINだったのに対して、今回は少しロジックの要素を加えました。より具体的にCode Pushdownを感じ取って頂けると確信しています。(謎)
  • 前回の子供だましのような単純な名称補完はやめます。うっかりグルグル回りたくなる(Loopによるロジック)ような要件です。回りたくなってきたでしょ(笑)
  • 仮想要件としては、EKKO:購買発注ヘッダ、EKPO:購買発注明細に対して、以下を結合します。
    • A:納入日程行の最新納品日のみを集計した値
      • A-1:ヘッダ、明細、納品日ごとに集計する
      • A-2:最新納品日を特定して抽出する
    • B:得意先の値は、ヘッダの設定値、明細の設定値があります。明細の設定値を優先する
      • B-1:ヘッダの設定値、明細の設定値を取得する
      • B-2:明細の設定値の有無を判定して、明細の設定値→ヘッダの設定値の優先順で値を出力する

ソースコードは4種類用意しました。(α:アルファ:β:ベータ:γ:ガンマ:λ:ラムダ)

  • α:渾身のSQL、WITH句使用、一発回答、ドン引きレヴェルのCode Pushdown
    • これを社内で公開したときは、非難されまくり。でもね。エラーロジック大幅カットでソースコードの健康を保つのは容易なのでは?一発回答なのでスパゲッティも1本のみ、絡まりようが無いです。究極のシンプル。(爆)
    • ちなみに、同一ロジックをABAP CDS Viewで記述すると、3つのViewを作ることになります。おそらくですが、New Open SQLの方が高速だと思います。理由としては、ABAP CDS Viewに選択条件をWhere句を使ってフィルタしようとすると、最終的な結果セットに対して効くような記述しかできないので(イロイロ方法はありますがRANGEは通常無理ですよね。)、最悪HDBは全件取得後にようやくWhere句が効いてくるのです。そうは言っても、全件取得でも両方なパフォーマンスが得られるロジックにすればいいだけ(若干乱暴ですが)だと思います。一方でNew Open SQLの場合は、好きなフェーズでWhere句を記述すればよいので、明示的にローレベルの段階から選択条件を効かせることが出来るのです。そうはいっても、HANA Information Viewで普通(これが難しいのですが)に実装するのが最速と思います。
    • (2022-01-07:追記)っと書きましたが、もっと高速なロジックを見つけました。後日公開します。ちょっとびっくりしました。私は一旦記事を書いた後でClassic Open SQL(というかABAP 7.31)でどこまでCode Pushdown出来るのか、試すことが多いのです。そして今回も試行錯誤していたのです。実際測定してみると、2.1[sec]だったのです。今後、一覧取得系のABAP Reportプログラムのデータ取得ロジックはこの考え方を使おうかなって思わせるほど、ドラスティックな発見でした。心配事は100人ABAPerが居て、何人が理解してこの実装を許してくれるかどうか?「規約違反はしていないけれどダメ」って品管に言われかねないほど、斬新かつシンプル(Classic Open SQLは出来ることが少ないからね)なんです。
    • パフォーマンス良好
  • β:折衷案その一、混乱の元「FOR ALL ENTRIES IN」を排除、内部テーブルのバケツリレーも排除
  • γ:折衷案その二、折衷案その一と同一ロジックで「FOR ALL ENTRIES IN」を使用。
    • 処理Aは「FOR ALL ENTRIES IN」を使用して従来実装として、処理BのみCode Pushdown。ITABのバケツリレーも排除。これだけでも大幅にスッキリしました。
    • ソースコード比較の時しか「FOR ALL ENTRIES IN」は書かないので、毎回書き方を思い出しています。使い方合っているかな?集計するのにLOOP AT GROUPを使っています。やっぱりブレーク使わないと駄目でしょうか?実装が大変なので勘弁してください。
    • 「FOR ALL ENTRIES IN」中堅以上が好きなんだよね~。なんかさぁ~ABAPビギナーに偉そうに言えるじゃない?でもさ、もう眉間にしわを寄せて偉そうに語るのもうやめようよ。大体さービジネス要件と無関係なABAP言語都合のロジックが多すぎるんだよ。エラーロジックが増えてしまって1本また1本とスパゲティの本数が増えるんだよ。絡まったらソースコードも関係者も体調崩すよ。そういうのごみ箱に捨てて働き方改革しようよ。
    • パフォーマンスはどうでしょう?微妙。
  • λ:レガシー実装のイメージ。ITABのバケツリレーも再現。
    • 実装大変でした。最後に各実装が同じ結果を出力するかどうか比較テストをしました。比較テストがOKにも関わらず、表面化しないバグをいくつか発見する始末。はっきり言って無理ゲーです。いまだに合っているのか自信が無いです。スパゲッテーの本数は大幅増量でお腹いっぱいです。ソースコードをコントロールするのが難しいので、ついITABのバケツリレーやっちゃうんですよね。そんなのも再現しました。ふ~。
    • パフォーマンス?なにそれ?オイシイの?って感じ。
  • 参考までパフォーマンス比較:実行時間は10回の平均値
    • 実行条件:データボリューム
      • EKKO:452000件
      • EKPO:453300件
      • EKET:459700件
      • EKPA:383800件
      • 出力結果:453300件
    • α:渾身のSQL、一発回答、ドン引きレヴェルのCode Pushdown
      • 2.79[SEC]
    • β:折衷案その一、混乱の元「FOR ALL ENTRIES IN」を排除、内部テーブルのバケツリレーも排除
      • 3.84[SEC]
    • γ:折衷案その二、折衷案その一と同一ロジックで「FOR ALL ENTRIES IN」を使用。
      • 5.40[SEC]
    • λ:レガシー実装のイメージ。ITABのバケツリレーも再現。
      • 7.38[SEC]

以下ソースコードです。どうぞ!
2022-01-07:ソースコード一部修正:結果セットを表示する場合はチェックボックスにチェックを付けて下さい。
2023-08-14:ソースコード一部修正:4本に分かれていたプログラムを1本にまとめました。微修正等

*&---------------------------------------------------------------------*
*& Report YSQL_CONSOLE
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT YSQL_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_ALPHA RADIOBUTTON GROUP RB1,                 "α:渾身のSQL、WITH句使用、一発回答、ドン引きレヴェルのCode Pushdown
  P_BATA  RADIOBUTTON GROUP RB1,                 "β:折衷案その一、混乱の元「FOR ALL ENTRIES IN」を排除、内部テーブルのバケツリレーも排除
  P_GAMMA RADIOBUTTON GROUP RB1,                 "γ:折衷案その二、折衷案その一と同一ロジックで「FOR ALL ENTRIES IN」を使用。
  P_DELTA RADIOBUTTON GROUP RB1.                 "λ:レガシー実装のイメージ。ITABのバケツリレーも再現。

DATA GDF_EBELN TYPE EKPO-EBELN.
TYPES GTR_EBELN LIKE RANGE OF GDF_EBELN.
SELECT-OPTIONS S_EBELN FOR GDF_EBELN.             "購買伝票番号

DATA GDF_EBELP TYPE EKPO-EBELP.
TYPES GTR_EBELP LIKE RANGE OF GDF_EBELP.
SELECT-OPTIONS S_EBELP FOR GDF_EBELP.             "購買伝票の明細番号

DATA GDF_MATNR TYPE EKPO-MATNR.
TYPES GTR_MATNR LIKE RANGE OF GDF_MATNR.
SELECT-OPTIONS S_MATNR FOR GDF_MATNR.             "品目コード

DATA GDF_WERKS TYPE EKPO-WERKS.
TYPES GTR_WERKS LIKE RANGE OF GDF_WERKS.
SELECT-OPTIONS S_WERKS FOR GDF_WERKS.             "プラント

DATA GDF_LGORT TYPE EKPO-LGORT.
TYPES GTR_LGORT LIKE RANGE OF GDF_LGORT.
SELECT-OPTIONS S_LGORT FOR GDF_LGORT.             "保管場所

*-----------------------------------------------------------------------
* START-OF-SELECTION
*-----------------------------------------------------------------------
START-OF-SELECTION.
  CASE ABAP_TRUE.
    WHEN P_ALPHA.
      PERFORM START_OF_SELECTION_ALPHA
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
          S_EBELN[]                               "購買伝票番号
          S_EBELP[]                               "購買伝票の明細番号
          S_MATNR[]                               "品目コード
          S_WERKS[]                               "プラント
          S_LGORT[]                               "保管場所
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_BATA.
      PERFORM START_OF_SELECTION_BATA
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
          S_EBELN[]                               "購買伝票番号
          S_EBELP[]                               "購買伝票の明細番号
          S_MATNR[]                               "品目コード
          S_WERKS[]                               "プラント
          S_LGORT[]                               "保管場所
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_GAMMA.
      PERFORM START_OF_SELECTION_GAMMA
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
          S_EBELN[]                               "購買伝票番号
          S_EBELP[]                               "購買伝票の明細番号
          S_MATNR[]                               "品目コード
          S_WERKS[]                               "プラント
          S_LGORT[]                               "保管場所
        CHANGING
          GDS_BAPIRET2                            "リターンパラメータ
          GDF_SUBRC.                              "リターンコード
      IF GDF_SUBRC <> 0.                          "リターンコード
        LEAVE TO LIST-PROCESSING.
      ENDIF.
    WHEN P_DELTA.
      PERFORM START_OF_SELECTION_DELTA
        USING
          P_ISDISP                                "表示フラグ
          P_TBMAXS                                "選択するエントリの最大数
          S_EBELN[]                               "購買伝票番号
          S_EBELP[]                               "購買伝票の明細番号
          S_MATNR[]                               "品目コード
          S_WERKS[]                               "プラント
          S_LGORT[]                               "保管場所
        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_ALPHA
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
      PIR_EBELN TYPE GTR_EBELN
      PIR_EBELP TYPE GTR_EBELP
      PIR_MATNR TYPE GTR_MATNR
      PIR_WERKS TYPE GTR_WERKS
      PIR_LGORT TYPE GTR_LGORT
    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_MESSAGE TYPE BAPIRET2-MESSAGE.            "メッセージテキスト

  "ITABのバケツリレーを防止するため、出力結果セットを最初から定義する
  "出力データの構造、テーブルを定義する
  TYPES:
    BEGIN OF LTS_DATA,
      EBELN         TYPE EKPO-EBELN,              "購買伝票番号
      EBELP         TYPE EKPO-EBELP,              "購買伝票の明細番号
      MATNR         TYPE EKPO-MATNR,              "品目コード
      TXZ01         TYPE EKPO-TXZ01,              "テキスト (短)
      WERKS         TYPE EKPO-WERKS,              "プラント
      LGORT         TYPE EKPO-LGORT,              "保管場所
      EKPO_MENGE    TYPE EKPO-MENGE,              "購買発注量
      MEINS         TYPE EKPO-MEINS,              "購買発注数量単位
      NETPR         TYPE EKPO-NETPR,              "購買伝票の正味価格 (伝票通貨)
      WAERS         TYPE EKKO-WAERS,              "通貨コード
      EINDT         TYPE EKET-EINDT,              "明細納入期日
      EKET_MENGE    TYPE EKET-MENGE,              "計画数量
      AMENG         TYPE EKET-AMENG,              "前数量 (納入日程行)
      WEMNG         TYPE EKET-WEMNG,              "入庫数量
      WAMNG         TYPE EKET-WAMNG,              "出庫量
      EKPA_LF_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
      EKPA_RS_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA,
    LDS_DATA LIKE LINE OF LDT_DATA.

  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  WITH
  "購買伝票ヘッダ、購買伝票明細、分納契約納入日程行からデータを取得する
  "明細、納入期日単位にレコード取得して、
  "明細の購買発注量はそのまま出力し分納契約納入日程行の各数量は集計する
  +EKPO AS ( SELECT
    FROM EKPO                                     "購買伝票明細
    INNER JOIN EKKO                               "購買伝票ヘッダ
       ON EKKO~EBELN = EKPO~EBELN                 "購買伝票番号
    LEFT OUTER JOIN EKET                          "分納契約納入日程行
       ON EKET~EBELN = EKPO~EBELN                 "購買伝票番号
      AND EKET~EBELP = EKPO~EBELP                 "購買伝票の明細番号
    FIELDS
      EKPO~EBELN,                                 "購買伝票番号
      EKPO~EBELP,                                 "購買伝票の明細番号
      EKPO~MATNR,                                 "品目コード
      EKPO~TXZ01,                                 "テキスト (短)
      EKPO~WERKS,                                 "プラント
      EKPO~LGORT,                                 "保管場所
      EKPO~MENGE AS EKPO_MENGE,                   "購買発注量
      EKPO~MEINS,                                 "購買発注数量単位
      EKPO~NETPR,                                 "購買伝票の正味価格 (伝票通貨)
      EKKO~WAERS,                                 "通貨コード
      CAST( COALESCE( EKET~EINDT,'00000000' ) AS DATS ) AS EINDT,
                                                  "明細納入期日
      SUM( EKET~MENGE ) AS EKET_MENGE,            "計画数量
      SUM( EKET~AMENG ) AS AMENG,                 "前数量 (納入日程行)
      SUM( EKET~WEMNG ) AS WEMNG,                 "入庫数量
      SUM( EKET~WAMNG ) AS WAMNG                  "出庫量
    WHERE EKPO~EBELN IN @PIR_EBELN                "購買伝票番号
      AND EKPO~EBELP IN @PIR_EBELP                "購買伝票の明細番号
      AND EKPO~MATNR IN @PIR_MATNR                "品目コード
      AND EKPO~WERKS IN @PIR_WERKS                "プラント
      AND EKPO~LGORT IN @PIR_LGORT                "保管場所
    GROUP BY
      EKPO~EBELN,                                 "購買伝票番号
      EKPO~EBELP,                                 "購買伝票の明細番号
      EKPO~MATNR,                                 "品目コード
      EKPO~TXZ01,                                 "テキスト (短)
      EKPO~WERKS,                                 "プラント
      EKPO~LGORT,                                 "保管場所
      EKPO~MENGE,                                 "購買発注量
      EKPO~MEINS,                                 "購買発注数量単位
      EKPO~NETPR,                                 "購買伝票の正味価格 (伝票通貨)
      EKKO~WAERS,                                 "通貨コード
      EKET~EINDT                                  "明細納入期日
  ),
  "分納契約納入日程行の納入期日が最新のキー情報を取得する
  +EKET AS ( SELECT
    FROM +EKPO
    FIELDS
      +EKPO~EBELN,                                "購買伝票番号
      +EKPO~EBELP,                                "購買伝票の明細番号
      MAX( +EKPO~EINDT ) AS EINDT                 "明細納入期日
    GROUP BY
      +EKPO~EBELN,                                "購買伝票番号
      +EKPO~EBELP                                 "購買伝票の明細番号
  )
  "分納契約納入日程行の納入期日が最新のレコードのみ出力する
  "購買管理における取引先機能(明細)を取得し、
  "購買管理における取引先機能(明細)より取得できない場合は、
  "購買管理における取引先機能(ヘッダ)より取得する
  SELECT
    FROM +EKPO
    INNER JOIN +EKET
       ON +EKET~EBELN = +EKPO~EBELN               "購買伝票番号
      AND +EKET~EBELP = +EKPO~EBELP               "購買伝票の明細番号
      AND +EKET~EINDT = +EKPO~EINDT               "明細納入期日
    LEFT OUTER JOIN EKPA AS EKPA_LF_I             "購買管理における取引先機能
       ON EKPA_LF_I~EBELN = +EKPO~EBELN           "購買伝票番号
      AND EKPA_LF_I~EBELP = +EKPO~EBELP           "購買伝票の明細番号
      AND EKPA_LF_I~PARVW = 'LF'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_LF_H             "購買管理における取引先機能
       ON EKPA_LF_H~EBELN = +EKPO~EBELN           "購買伝票番号
      AND EKPA_LF_H~EBELP IS INITIAL              "購買伝票の明細番号
      AND EKPA_LF_H~PARVW = 'LF'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_RS_I             "購買管理における取引先機能
       ON EKPA_RS_I~EBELN = +EKPO~EBELN           "購買伝票番号
      AND EKPA_RS_I~EBELP = +EKPO~EBELP           "購買伝票の明細番号
      AND EKPA_RS_I~PARVW = 'RS'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_RS_H             "購買管理における取引先機能
       ON EKPA_RS_H~EBELN = +EKPO~EBELN           "購買伝票番号
      AND EKPA_RS_H~EBELP IS INITIAL              "購買伝票の明細番号
      AND EKPA_RS_H~PARVW = 'RS'                  "取引先機能
    FIELDS
      +EKPO~EBELN,                                "購買伝票番号
      +EKPO~EBELP,                                "購買伝票の明細番号
      +EKPO~MATNR,                                "品目コード
      +EKPO~TXZ01,                                "テキスト (短)
      +EKPO~WERKS,                                "プラント
      +EKPO~LGORT,                                "保管場所
      +EKPO~EKPO_MENGE,                           "購買発注量
      +EKPO~MEINS,                                "購買発注数量単位
      +EKPO~NETPR,                                "購買伝票の正味価格 (伝票通貨)
      +EKPO~WAERS,                                "通貨コード
      +EKPO~EINDT,                                "明細納入期日
      +EKPO~EKET_MENGE,                           "計画数量
      +EKPO~AMENG,                                "前数量 (納入日程行)
      +EKPO~WEMNG,                                "入庫数量
      +EKPO~WAMNG,                                "出庫量
      COALESCE( EKPA_LF_I~LIFN2,EKPA_LF_H~LIFN2 ) AS EKPA_LF_LIFN2,
                                                  "他の仕入先への参照
      COALESCE( EKPA_RS_I~LIFN2,EKPA_RS_H~LIFN2 ) AS EKPA_RS_LIFN2
                                                  "他の仕入先への参照
    ORDER BY
      +EKPO~EBELN,                                "購買伝票番号
      +EKPO~EBELP                                 "購買伝票の明細番号
    INTO TABLE @LDT_DATA UP TO 1000000 ROWS.
  IF SY-SUBRC <> 0.
    POS_BAPIRET2 = VALUE #(
        TYPE = SY-MSGTY
        ID = SY-MSGID
        NUMBER = SY-MSGNO
        MESSAGE = LDF_MESSAGE
        MESSAGE_V1 = SY-MSGV1
        MESSAGE_V2 = SY-MSGV2
        MESSAGE_V3 = SY-MSGV3
        MESSAGE_V4 = SY-MSGV4 ).
    POF_SUBRC = 4.
    RETURN.
  ENDIF.

  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 = 'EKKO' ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 4 TEXT = 'EKPO' ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_COUNT5).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 5 TEXT = LDF_COUNT5 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 6 TEXT = 'EKET' ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_COUNT7).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 7 TEXT = LDF_COUNT7 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 8 TEXT = 'EKPA' ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_COUNT9).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 9 TEXT = LDF_COUNT9 ).
  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_BATA
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
      PIR_EBELN TYPE GTR_EBELN
      PIR_EBELP TYPE GTR_EBELP
      PIR_MATNR TYPE GTR_MATNR
      PIR_WERKS TYPE GTR_WERKS
      PIR_LGORT TYPE GTR_LGORT
    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_MESSAGE TYPE BAPIRET2-MESSAGE.            "メッセージテキスト

  "ITABのバケツリレーを防止するため、出力結果セットを最初から定義する
  "出力データの構造、テーブルを定義する
  TYPES:
    BEGIN OF LTS_DATA,
      EBELN         TYPE EKPO-EBELN,              "購買伝票番号
      EBELP         TYPE EKPO-EBELP,              "購買伝票の明細番号
      MATNR         TYPE EKPO-MATNR,              "品目コード
      TXZ01         TYPE EKPO-TXZ01,              "テキスト (短)
      WERKS         TYPE EKPO-WERKS,              "プラント
      LGORT         TYPE EKPO-LGORT,              "保管場所
      EKPO_MENGE    TYPE EKPO-MENGE,              "購買発注量
      MEINS         TYPE EKPO-MEINS,              "購買発注数量単位
      NETPR         TYPE EKPO-NETPR,              "購買伝票の正味価格 (伝票通貨)
      WAERS         TYPE EKKO-WAERS,              "通貨コード
      EINDT         TYPE EKET-EINDT,              "明細納入期日
      EKET_MENGE    TYPE EKET-MENGE,              "計画数量
      AMENG         TYPE EKET-AMENG,              "前数量 (納入日程行)
      WEMNG         TYPE EKET-WEMNG,              "入庫数量
      WAMNG         TYPE EKET-WAMNG,              "出庫量
      EKPA_LF_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
      EKPA_RS_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA,
    LDS_DATA LIKE LINE OF LDT_DATA.

  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "購買伝票ヘッダ、購買伝票明細より、明細単位で情報を取得する。
  SELECT
    FROM EKPO                                     "購買伝票明細
    INNER JOIN EKKO                               "購買伝票ヘッダ
       ON EKKO~EBELN = EKPO~EBELN                 "購買伝票番号
    LEFT OUTER JOIN EKPA AS EKPA_LF_H             "購買管理における取引先機能
       ON EKPA_LF_H~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_LF_H~EBELP IS INITIAL              "購買伝票の明細番号
      AND EKPA_LF_H~PARVW = 'LF'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_LF_I             "購買管理における取引先機能
       ON EKPA_LF_I~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_LF_I~EBELP = EKPO~EBELP            "購買伝票の明細番号
      AND EKPA_LF_I~PARVW = 'LF'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_RS_H             "購買管理における取引先機能
       ON EKPA_RS_H~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_RS_H~EBELP IS INITIAL              "購買伝票の明細番号
      AND EKPA_RS_H~PARVW = 'RS'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_RS_I             "購買管理における取引先機能
       ON EKPA_RS_I~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_RS_I~EBELP = EKPO~EBELP            "購買伝票の明細番号
      AND EKPA_RS_I~PARVW = 'RS'                  "取引先機能
    FIELDS
      EKPO~EBELN,                                 "購買伝票番号
      EKPO~EBELP,                                 "購買伝票の明細番号
      EKPO~MATNR,                                 "品目コード
      EKPO~TXZ01,                                 "テキスト (短)
      EKPO~WERKS,                                 "プラント
      EKPO~LGORT,                                 "保管場所
      EKPO~MENGE AS EKPO_MENGE,                   "購買発注量
      EKPO~MEINS,                                 "購買発注数量単位
      EKPO~NETPR,                                 "購買伝票の正味価格 (伝票通貨)
      EKKO~WAERS,                                  "通貨コード
      COALESCE( EKPA_LF_I~LIFN2,EKPA_LF_H~LIFN2 ) AS EKPA_LF_LIFN2,
                                                  "他の仕入先への参照
      COALESCE( EKPA_RS_I~LIFN2,EKPA_RS_H~LIFN2 ) AS EKPA_RS_LIFN2
                                                  "他の仕入先への参照
    WHERE EKPO~EBELN IN @PIR_EBELN                "購買伝票番号
      AND EKPO~EBELP IN @PIR_EBELP                "購買伝票の明細番号
      AND EKPO~MATNR IN @PIR_MATNR                "品目コード
      AND EKPO~WERKS IN @PIR_WERKS                "プラント
      AND EKPO~LGORT IN @PIR_LGORT                "保管場所
    ORDER BY
      EKPO~EBELN,                                 "購買伝票番号
      EKPO~EBELP                                  "購買伝票の明細番号
    INTO CORRESPONDING FIELDS OF TABLE @LDT_DATA.
  IF SY-SUBRC <> 0.
    POS_BAPIRET2 = VALUE #(
        TYPE = SY-MSGTY
        ID = SY-MSGID
        NUMBER = SY-MSGNO
        MESSAGE = LDF_MESSAGE
        MESSAGE_V1 = SY-MSGV1
        MESSAGE_V2 = SY-MSGV2
        MESSAGE_V3 = SY-MSGV3
        MESSAGE_V4 = SY-MSGV4 ).
    POF_SUBRC = 4.
    RETURN.
  ENDIF.

  "購買伝票ヘッダ、購買伝票明細以外のテーブルより取得する情報を各ITABに格納する
  "購買伝票ヘッダ、購買伝票明細に関連する分納契約納入日程行を取得する
  "該当するレコードがない場合は、ITABの処理をパススルーして処理を続行する
  SELECT
    FROM EKET                                     "分納契約納入日程行
    INNER JOIN @LDT_DATA AS LDT_DATA
       ON LDT_DATA~EBELN = EKET~EBELN             "購買伝票番号
      AND LDT_DATA~EBELP = EKET~EBELP             "購買伝票の明細番号
    FIELDS
      EKET~EBELN,                                 "購買伝票番号
      EKET~EBELP,                                 "購買伝票の明細番号
      EKET~EINDT,                                 "明細納入期日
      SUM( EKET~MENGE ) AS EKET_MENGE,            "計画数量
      SUM( EKET~AMENG ) AS AMENG,                 "前数量 (納入日程行)
      SUM( EKET~WEMNG ) AS WEMNG,                 "入庫数量
      SUM( EKET~WAMNG ) AS WAMNG                  "出庫量
    GROUP BY
      EKET~EBELN,                                 "購買伝票番号
      EKET~EBELP,                                 "購買伝票の明細番号
      EKET~EINDT                                  "明細納入期日
    ORDER BY
      EKET~EBELN ASCENDING,                       "購買伝票番号
      EKET~EBELP ASCENDING,                       "購買伝票の明細番号
      EKET~EINDT DESCENDING                       "明細納入期日
    INTO TABLE @DATA(LDT_EKET).
  IF SY-SUBRC = 0.
    "分納契約納入日程行の納入期日が最新のレコードのみを残す
    "(BINARY SEARCHの準備)
    DELETE ADJACENT DUPLICATES FROM LDT_EKET COMPARING
        EBELN                                     "購買伝票番号
        EBELP.                                    "購買伝票の明細番号
  ENDIF.

  "購買伝票ヘッダ、購買伝票明細より、明細単位で情報をITABより取得する
  "明細行に対応する、分納契約納入日程行の納入期日の最新の情報をITABより取得する
  "ITABのバケツリレーはやらない
  LOOP AT LDT_DATA ASSIGNING FIELD-SYMBOL(<LFS_DATA>).
    "明細行に対応する、分納契約納入日程行の納入期日の最新の情報をITABより取得する
    "該当するレコードがない場合は、取得をパススルーして処理を続行する
    READ TABLE LDT_EKET INTO DATA(LDS_EKET)
        WITH KEY
          EBELN = <LFS_DATA>-EBELN                "購買伝票番号
          EBELP = <LFS_DATA>-EBELP                "購買伝票の明細番号
        BINARY SEARCH.
    IF SY-SUBRC = 0.
      <LFS_DATA>-EINDT = LDS_EKET-EINDT.          "明細納入期日
      <LFS_DATA>-EKET_MENGE = LDS_EKET-EKET_MENGE."計画数量
      <LFS_DATA>-AMENG = LDS_EKET-AMENG.          "前数量 (納入日程行)
      <LFS_DATA>-WEMNG = LDS_EKET-WEMNG.          "入庫数量
      <LFS_DATA>-WAMNG = LDS_EKET-WAMNG.          "出庫量
    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 = 'EKKO' ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 4 TEXT = 'EKPO' ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_COUNT5).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 5 TEXT = LDF_COUNT5 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 6 TEXT = 'EKET' ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_COUNT7).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 7 TEXT = LDF_COUNT7 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 8 TEXT = 'EKPA' ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_COUNT9).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 9 TEXT = LDF_COUNT9 ).
  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_GAMMA
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
      PIR_EBELN TYPE GTR_EBELN
      PIR_EBELP TYPE GTR_EBELP
      PIR_MATNR TYPE GTR_MATNR
      PIR_WERKS TYPE GTR_WERKS
      PIR_LGORT TYPE GTR_LGORT
    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_MESSAGE TYPE BAPIRET2-MESSAGE.            "メッセージテキスト

  "ITABのバケツリレーを防止するため、出力結果セットを最初から定義する
  "出力データの構造、テーブルを定義する
  TYPES:
    BEGIN OF LTS_DATA,
      EBELN         TYPE EKPO-EBELN,              "購買伝票番号
      EBELP         TYPE EKPO-EBELP,              "購買伝票の明細番号
      MATNR         TYPE EKPO-MATNR,              "品目コード
      TXZ01         TYPE EKPO-TXZ01,              "テキスト (短)
      WERKS         TYPE EKPO-WERKS,              "プラント
      LGORT         TYPE EKPO-LGORT,              "保管場所
      EKPO_MENGE    TYPE EKPO-MENGE,              "購買発注量
      MEINS         TYPE EKPO-MEINS,              "購買発注数量単位
      NETPR         TYPE EKPO-NETPR,              "購買伝票の正味価格 (伝票通貨)
      WAERS         TYPE EKKO-WAERS,              "通貨コード
      EINDT         TYPE EKET-EINDT,              "明細納入期日
      EKET_MENGE    TYPE EKET-MENGE,              "計画数量
      AMENG         TYPE EKET-AMENG,              "前数量 (納入日程行)
      WEMNG         TYPE EKET-WEMNG,              "入庫数量
      WAMNG         TYPE EKET-WAMNG,              "出庫量
      EKPA_LF_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
      EKPA_RS_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA,
    LDS_DATA LIKE LINE OF LDT_DATA.

  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "購買伝票ヘッダ、購買伝票明細より、明細単位で情報を取得する。
  SELECT
    FROM EKPO                                     "購買伝票明細
    INNER JOIN EKKO                               "購買伝票ヘッダ
       ON EKKO~EBELN = EKPO~EBELN                 "購買伝票番号
    LEFT OUTER JOIN EKPA AS EKPA_LF_H             "購買管理における取引先機能
       ON EKPA_LF_H~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_LF_H~EBELP IS INITIAL              "購買伝票の明細番号
      AND EKPA_LF_H~PARVW = 'LF'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_LF_I             "購買管理における取引先機能
       ON EKPA_LF_I~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_LF_I~EBELP = EKPO~EBELP            "購買伝票の明細番号
      AND EKPA_LF_I~PARVW = 'LF'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_RS_H             "購買管理における取引先機能
       ON EKPA_RS_H~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_RS_H~EBELP IS INITIAL              "購買伝票の明細番号
      AND EKPA_RS_H~PARVW = 'RS'                  "取引先機能
    LEFT OUTER JOIN EKPA AS EKPA_RS_I             "購買管理における取引先機能
       ON EKPA_RS_I~EBELN = EKPO~EBELN            "購買伝票番号
      AND EKPA_RS_I~EBELP = EKPO~EBELP            "購買伝票の明細番号
      AND EKPA_RS_I~PARVW = 'RS'                  "取引先機能
    FIELDS
      EKPO~EBELN,                                 "購買伝票番号
      EKPO~EBELP,                                 "購買伝票の明細番号
      EKPO~MATNR,                                 "品目コード
      EKPO~TXZ01,                                 "テキスト (短)
      EKPO~WERKS,                                 "プラント
      EKPO~LGORT,                                 "保管場所
      EKPO~MENGE AS EKPO_MENGE,                   "購買発注量
      EKPO~MEINS,                                 "購買発注数量単位
      EKPO~NETPR,                                 "購買伝票の正味価格 (伝票通貨)
      EKKO~WAERS,                                  "通貨コード
      COALESCE( EKPA_LF_I~LIFN2,EKPA_LF_H~LIFN2 ) AS EKPA_LF_LIFN2,
                                                  "他の仕入先への参照
      COALESCE( EKPA_RS_I~LIFN2,EKPA_RS_H~LIFN2 ) AS EKPA_RS_LIFN2
                                                  "他の仕入先への参照
    WHERE EKPO~EBELN IN @PIR_EBELN                "購買伝票番号
      AND EKPO~EBELP IN @PIR_EBELP                "購買伝票の明細番号
      AND EKPO~MATNR IN @PIR_MATNR                "品目コード
      AND EKPO~WERKS IN @PIR_WERKS                "プラント
      AND EKPO~LGORT IN @PIR_LGORT                "保管場所
    ORDER BY
      EKPO~EBELN,                                 "購買伝票番号
      EKPO~EBELP                                  "購買伝票の明細番号
    INTO CORRESPONDING FIELDS OF TABLE @LDT_DATA.
  IF SY-SUBRC <> 0.
    POS_BAPIRET2 = VALUE #(
        TYPE = SY-MSGTY
        ID = SY-MSGID
        NUMBER = SY-MSGNO
        MESSAGE = LDF_MESSAGE
        MESSAGE_V1 = SY-MSGV1
        MESSAGE_V2 = SY-MSGV2
        MESSAGE_V3 = SY-MSGV3
        MESSAGE_V4 = SY-MSGV4 ).
    POF_SUBRC = 4.
    RETURN.
  ENDIF.

  "購買伝票ヘッダ、購買伝票明細以外のテーブルより取得する情報を各ITABに格納する
  IF LDT_DATA IS NOT INITIAL.
    "購買伝票明細単位に、重複を削除する。(FOR ALL ENTRIES INの準備)
    DATA(LDT_EKPO_HI) = LDT_DATA.
    SORT LDT_EKPO_HI ASCENDING BY
        EBELN                                     "購買伝票番号
        EBELP.                                    "購買伝票の明細番号
    DELETE ADJACENT DUPLICATES FROM LDT_EKPO_HI COMPARING
        EBELN                                     "購買伝票番号
        EBELP.                                    "購買伝票の明細番号

    "購買伝票ヘッダ、購買伝票明細に関連する分納契約納入日程行を取得する
    "該当するレコードがない場合は、ITABの処理をパススルーして処理を続行する
    SELECT
        EKET~EBELN,                               "購買伝票番号
        EKET~EBELP,                               "購買伝票の明細番号
        EKET~EINDT,                               "明細納入期日
        EKET~MENGE AS EKET_MENGE,                 "計画数量
        EKET~AMENG,                               "前数量 (納入日程行)
        EKET~WEMNG,                               "入庫数量
        EKET~WAMNG                                "出庫量
      FROM EKET                                   "分納契約納入日程行
      FOR ALL ENTRIES IN @LDT_EKPO_HI
      WHERE EKET~EBELN = @LDT_EKPO_HI-EBELN       "購買伝票番号
        AND EKET~EBELP = @LDT_EKPO_HI-EBELP       "購買伝票の明細番号
      INTO TABLE @DATA(LDT_EKET_1).
    IF SY-SUBRC = 0.
      "分納契約納入日程行の各数量を納入期日単位に集計する
      "(FOR ALL ENTRIES INでは、集合関数(SUM())が使えないためループ処理で集計する)
      DATA:
        LDT_EKET LIKE LDT_EKET_1.
      LOOP AT LDT_EKET_1 ASSIGNING FIELD-SYMBOL(<LFS_ELET_I>)
          GROUP BY (
            EBELN = <LFS_ELET_I>-EBELN            "購買伝票番号
            EBELP = <LFS_ELET_I>-EBELP            "購買伝票の明細番号
            EINDT = <LFS_ELET_I>-EINDT ).         "明細納入期日
        DATA(LDS_EKET_I) = <LFS_ELET_I>.
        CLEAR:
          LDS_EKET_I-EKET_MENGE,                  "計画数量
          LDS_EKET_I-AMENG,                       "前数量 (納入日程行)
          LDS_EKET_I-WEMNG,                       "入庫数量
          LDS_EKET_I-WAMNG.                       "出庫量

        LOOP AT GROUP <LFS_ELET_I> ASSIGNING FIELD-SYMBOL(<LFS_ELET_J>).
          LDS_EKET_I-EKET_MENGE = LDS_EKET_I-EKET_MENGE + <LFS_ELET_J>-EKET_MENGE."計画数量
          LDS_EKET_I-AMENG = LDS_EKET_I-AMENG + <LFS_ELET_J>-AMENG."前数量 (納入日程行)
          LDS_EKET_I-WEMNG = LDS_EKET_I-WEMNG + <LFS_ELET_J>-WEMNG."入庫数量
          LDS_EKET_I-WAMNG = LDS_EKET_I-WAMNG + <LFS_ELET_J>-WAMNG."出庫量
        ENDLOOP.
        APPEND LDS_EKET_I TO LDT_EKET.
      ENDLOOP.

      "分納契約納入日程行の納入期日が最新のレコードのみを残す
      "後述処理でREAD TABLE~BINARY SEARCHを実行するために検索キーをソートする
      "(BINARY SEARCHの準備)
      SORT LDT_EKET BY
          EBELN ASCENDING                         "購買伝票番号
          EBELP ASCENDING                         "購買伝票の明細番号
          EINDT DESCENDING.                       "明細納入期日
      DELETE ADJACENT DUPLICATES FROM LDT_EKET COMPARING
          EBELN                                   "購買伝票番号
          EBELP.                                  "購買伝票の明細番号
    ENDIF.
  ENDIF.

  "購買伝票ヘッダ、購買伝票明細より、明細単位で情報をITABより取得する
  "明細行に対応する、分納契約納入日程行の納入期日の最新の情報をITABより取得する
  "ITABのバケツリレーはやらない
  LOOP AT LDT_DATA ASSIGNING FIELD-SYMBOL(<LFS_DATA>).
    "明細行に対応する、分納契約納入日程行の納入期日の最新の情報をITABより取得する
    "該当するレコードがない場合は、取得をパススルーして処理を続行する
    READ TABLE LDT_EKET INTO DATA(LDS_EKET)
        WITH KEY
          EBELN = <LFS_DATA>-EBELN                "購買伝票番号
          EBELP = <LFS_DATA>-EBELP                "購買伝票の明細番号
        BINARY SEARCH.
    IF SY-SUBRC = 0.
      <LFS_DATA>-EINDT = LDS_EKET-EINDT.          "明細納入期日
      <LFS_DATA>-EKET_MENGE = LDS_EKET-EKET_MENGE."計画数量
      <LFS_DATA>-AMENG = LDS_EKET-AMENG.          "前数量 (納入日程行)
      <LFS_DATA>-WEMNG = LDS_EKET-WEMNG.          "入庫数量
      <LFS_DATA>-WAMNG = LDS_EKET-WAMNG.          "出庫量
    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 = 'EKKO' ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 4 TEXT = 'EKPO' ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_COUNT5).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 5 TEXT = LDF_COUNT5 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 6 TEXT = 'EKET' ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_COUNT7).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 7 TEXT = LDF_COUNT7 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 8 TEXT = 'EKPA' ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_COUNT9).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 9 TEXT = LDF_COUNT9 ).
  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_DELTA
    USING
      PIF_ISDISP   TYPE CHECKBOX
      PIF_TBMAXSEL TYPE TBMAXSEL
      PIR_EBELN TYPE GTR_EBELN
      PIR_EBELP TYPE GTR_EBELP
      PIR_MATNR TYPE GTR_MATNR
      PIR_WERKS TYPE GTR_WERKS
      PIR_LGORT TYPE GTR_LGORT
    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_MESSAGE TYPE BAPIRET2-MESSAGE.            "メッセージテキスト

  DATA LDF_TIMESTAMP_START TYPE TIMESTAMPL.       "タイムスタンプデータ取得開始
  GET TIME STAMP FIELD LDF_TIMESTAMP_START.       "タイムスタンプデータ取得開始

  "購買伝票ヘッダ、購買伝票明細より、明細単位で情報を取得する。
  SELECT
      EKPO~EBELN,                                 "購買伝票番号
      EKPO~EBELP,                                 "購買伝票の明細番号
      EKPO~MATNR,                                 "品目コード
      EKPO~TXZ01,                                 "テキスト (短)
      EKPO~WERKS,                                 "プラント
      EKPO~LGORT,                                 "保管場所
      EKPO~MENGE AS EKPO_MENGE,                   "購買発注量
      EKPO~MEINS,                                 "購買発注数量単位
      EKPO~NETPR,                                 "購買伝票の正味価格 (伝票通貨)
      EKKO~WAERS                                  "通貨コード
    FROM EKKO                                     "購買伝票ヘッダ
    INNER JOIN EKPO                               "購買伝票明細
       ON EKPO~EBELN = EKKO~EBELN                 "購買伝票番号
    WHERE EKPO~EBELN IN @PIR_EBELN                "購買伝票番号
      AND EKPO~EBELP IN @PIR_EBELP                "購買伝票の明細番号
      AND EKPO~MATNR IN @PIR_MATNR                "品目コード
      AND EKPO~WERKS IN @PIR_WERKS                "プラント
      AND EKPO~LGORT IN @PIR_LGORT                "保管場所
    INTO TABLE @DATA(LDT_EKPO).
  IF SY-SUBRC <> 0.
    POS_BAPIRET2 = VALUE #(
        TYPE = SY-MSGTY
        ID = SY-MSGID
        NUMBER = SY-MSGNO
        MESSAGE = LDF_MESSAGE
        MESSAGE_V1 = SY-MSGV1
        MESSAGE_V2 = SY-MSGV2
        MESSAGE_V3 = SY-MSGV3
        MESSAGE_V4 = SY-MSGV4 ).
    POF_SUBRC = 4.
    RETURN.
  ENDIF.

  "購買伝票ヘッダ、購買伝票明細以外のテーブルより取得する情報を各ITABに格納する
  IF LDT_EKPO IS NOT INITIAL.
    "購買伝票明細単位に、重複を削除する。(FOR ALL ENTRIES INの準備)
    DATA(LDT_EKPO_HI) = LDT_EKPO.
    SORT LDT_EKPO_HI ASCENDING BY
        EBELN                                     "購買伝票番号
        EBELP.                                    "購買伝票の明細番号
    DELETE ADJACENT DUPLICATES FROM LDT_EKPO_HI COMPARING
        EBELN                                     "購買伝票番号
        EBELP.                                    "購買伝票の明細番号

    "購買伝票ヘッダ、購買伝票明細に関連する分納契約納入日程行を取得する
    "該当するレコードがない場合は、ITABの処理をパススルーして処理を続行する
    SELECT
        EKET~EBELN,                               "購買伝票番号
        EKET~EBELP,                               "購買伝票の明細番号
        EKET~EINDT,                               "明細納入期日
        EKET~MENGE AS EKET_MENGE,                 "計画数量
        EKET~AMENG,                               "前数量 (納入日程行)
        EKET~WEMNG,                               "入庫数量
        EKET~WAMNG                                "出庫量
      FROM EKET                                   "分納契約納入日程行
      FOR ALL ENTRIES IN @LDT_EKPO_HI
      WHERE EKET~EBELN = @LDT_EKPO_HI-EBELN       "購買伝票番号
        AND EKET~EBELP = @LDT_EKPO_HI-EBELP       "購買伝票の明細番号
      INTO TABLE @DATA(LDT_EKET_1).
    IF SY-SUBRC = 0.
      "分納契約納入日程行の各数量を納入期日単位に集計する
      "(FOR ALL ENTRIES INでは、集合関数(SUM())が使えないためループ処理で集計する)
      DATA:
        LDT_EKET LIKE LDT_EKET_1.
      LOOP AT LDT_EKET_1 ASSIGNING FIELD-SYMBOL(<LFS_ELET_I>)
          GROUP BY (
            EBELN = <LFS_ELET_I>-EBELN            "購買伝票番号
            EBELP = <LFS_ELET_I>-EBELP            "購買伝票の明細番号
            EINDT = <LFS_ELET_I>-EINDT ).         "明細納入期日
        DATA(LDS_EKET_I) = <LFS_ELET_I>.
        CLEAR:
          LDS_EKET_I-EKET_MENGE,                  "計画数量
          LDS_EKET_I-AMENG,                       "前数量 (納入日程行)
          LDS_EKET_I-WEMNG,                       "入庫数量
          LDS_EKET_I-WAMNG.                       "出庫量

        LOOP AT GROUP <LFS_ELET_I> ASSIGNING FIELD-SYMBOL(<LFS_ELET_J>).
          LDS_EKET_I-EKET_MENGE = LDS_EKET_I-EKET_MENGE + <LFS_ELET_J>-EKET_MENGE."計画数量
          LDS_EKET_I-AMENG = LDS_EKET_I-AMENG + <LFS_ELET_J>-AMENG."前数量 (納入日程行)
          LDS_EKET_I-WEMNG = LDS_EKET_I-WEMNG + <LFS_ELET_J>-WEMNG."入庫数量
          LDS_EKET_I-WAMNG = LDS_EKET_I-WAMNG + <LFS_ELET_J>-WAMNG."出庫量
        ENDLOOP.

        APPEND LDS_EKET_I TO LDT_EKET.
      ENDLOOP.

      "分納契約納入日程行の納入期日が最新のレコードのみを残す
      "後述処理でREAD TABLE~BINARY SEARCHを実行するために検索キーをソートする
      "(BINARY SEARCHの準備)
      SORT LDT_EKET BY
          EBELN ASCENDING                         "購買伝票番号
          EBELP ASCENDING                         "購買伝票の明細番号
          EINDT DESCENDING.                       "明細納入期日
      DELETE ADJACENT DUPLICATES FROM LDT_EKET COMPARING
          EBELN                                   "購買伝票番号
          EBELP.                                  "購買伝票の明細番号
    ENDIF.

    "購買伝票ヘッダ、購買伝票明細に関連する購買管理における取引先機能(明細)を取得する
    "該当するレコードがない場合は、ITABの処理をパススルーして処理を続行する
    SELECT
        EKPA~EBELN,                               "購買伝票番号
        EKPA~EBELP,                               "購買伝票の明細番号
        EKPA~PARVW,                               "取引先機能
        EKPA~LIFN2                                "他の仕入先への参照
      FROM EKPA                                   "購買管理における取引先機能
      FOR ALL ENTRIES IN @LDT_EKPO_HI
      WHERE EKPA~EBELN = @LDT_EKPO_HI-EBELN       "購買伝票番号
        AND EKPA~EBELP = @LDT_EKPO_HI-EBELP       "購買伝票の明細番号
        AND EKPA~PARVW IN ('LF','RS')             "取引先機能
      INTO TABLE @DATA(LDT_EKPA_HI).
    IF SY-SUBRC = 0.
      "後述処理でREAD TABLE~BINARY SEARCHを実行するために検索キーをソートする
      "(BINARY SEARCHの準備)
      SORT LDT_EKPA_HI ASCENDING BY
          EBELN                                   "購買伝票番号
          EBELP                                   "購買伝票の明細番号
          PARVW.                                  "取引先機能
      DELETE ADJACENT DUPLICATES FROM LDT_EKPA_HI COMPARING
          EBELN                                   "購買伝票番号
          EBELP                                   "購買伝票の明細番号
          PARVW.                                  "取引先機能
    ENDIF.

    "購買伝票ヘッダ単位に、重複を削除する。(FOR ALL ENTRIES INの準備)
    DATA(LDT_EKPO_H) = LDT_EKPO.
    SORT LDT_EKPO_H ASCENDING BY
        EBELN.                                    "購買伝票番号
    DELETE ADJACENT DUPLICATES FROM LDT_EKPO_HI COMPARING
        EBELN.                                    "購買伝票番号

    "購買伝票ヘッダ、購買伝票明細に関連する購買管理における取引先機能(ヘッダ)を取得する
    "該当するレコードがない場合は、ITABの処理をパススルーして処理を続行する
    SELECT
        EKPA~EBELN,                               "購買伝票番号
        EKPA~PARVW,                               "取引先機能
        EKPA~LIFN2                                "他の仕入先への参照
      FROM EKPA                                   "
      FOR ALL ENTRIES IN @LDT_EKPO_H
      WHERE EKPA~EBELN = @LDT_EKPO_H-EBELN        "購買伝票番号
        AND EKPA~EBELP IS INITIAL                 "購買伝票の明細番号
        AND EKPA~PARVW IN ('LF','RS')             "取引先機能
      INTO TABLE @DATA(LDT_EKPA_H).
    IF SY-SUBRC = 0.
      "後述処理でREAD TABLE~BINARY SEARCHを実行するために検索キーをソートする
      "(BINARY SEARCHの準備)
      SORT LDT_EKPA_H ASCENDING BY
          EBELN                                   "購買伝票番号
          PARVW.                                  "取引先機能
      DELETE ADJACENT DUPLICATES FROM LDT_EKPA_H COMPARING
          EBELN                                   "購買伝票番号
          PARVW.                                  "取引先機能
    ENDIF.
  ENDIF.

  "出力データの構造、テーブルを定義する
  TYPES:
    BEGIN OF LTS_DATA,
      EBELN         TYPE EKPO-EBELN,              "購買伝票番号
      EBELP         TYPE EKPO-EBELP,              "購買伝票の明細番号
      MATNR         TYPE EKPO-MATNR,              "品目コード
      TXZ01         TYPE EKPO-TXZ01,              "テキスト (短)
      WERKS         TYPE EKPO-WERKS,              "プラント
      LGORT         TYPE EKPO-LGORT,              "保管場所
      EKPO_MENGE    TYPE EKPO-MENGE,              "購買発注量
      MEINS         TYPE EKPO-MEINS,              "購買発注数量単位
      NETPR         TYPE EKPO-NETPR,              "購買伝票の正味価格 (伝票通貨)
      WAERS         TYPE EKKO-WAERS,              "通貨コード
      EINDT         TYPE EKET-EINDT,              "明細納入期日
      EKET_MENGE    TYPE EKET-MENGE,              "計画数量
      AMENG         TYPE EKET-AMENG,              "前数量 (納入日程行)
      WEMNG         TYPE EKET-WEMNG,              "入庫数量
      WAMNG         TYPE EKET-WAMNG,              "出庫量
      EKPA_LF_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
      EKPA_RS_LIFN2 TYPE EKPA-LIFN2,              "他の仕入先への参照
    END OF LTS_DATA,
    LTT_DATA TYPE STANDARD TABLE OF LTS_DATA.
  DATA:
    LDT_DATA TYPE LTT_DATA,
    LDS_DATA LIKE LINE OF LDT_DATA.

  "購買伝票ヘッダ、購買伝票明細より、明細単位で情報をITABより取得する
  "明細行に対応する、分納契約納入日程行の納入期日の最新の情報をITABより取得する
  "購買管理における取引先機能より、取引先をITABより取得する
  "バケツリレー式に、LDT_DATAにデータを格納する
  LOOP AT LDT_EKPO ASSIGNING FIELD-SYMBOL(<LFS_EKPO>).
    CLEAR LDS_DATA.

    "購買伝票ヘッダ、購買伝票明細より、明細単位で情報をITABより取得する
    LDS_DATA = VALUE LTS_DATA(
      EBELN = <LFS_EKPO>-EBELN                    "購買伝票番号
      EBELP = <LFS_EKPO>-EBELP                    "購買伝票の明細番号
      MATNR = <LFS_EKPO>-MATNR                    "品目コード
      TXZ01 = <LFS_EKPO>-TXZ01                    "テキスト (短)
      WERKS = <LFS_EKPO>-WERKS                    "プラント
      LGORT = <LFS_EKPO>-LGORT                    "保管場所
      EKPO_MENGE = <LFS_EKPO>-EKPO_MENGE          "購買発注量
      MEINS = <LFS_EKPO>-MEINS                    "購買発注数量単位
      NETPR = <LFS_EKPO>-NETPR                    "購買伝票の正味価格 (伝票通貨)
      WAERS = <LFS_EKPO>-WAERS ).                 "通貨コード

    "明細行に対応する、分納契約納入日程行の納入期日の最新の情報をITABより取得する
    "該当するレコードがない場合は、取得をパススルーして処理を続行する
    READ TABLE LDT_EKET INTO DATA(LDS_EKET)
        WITH KEY
          EBELN = <LFS_EKPO>-EBELN                "購買伝票番号
          EBELP = <LFS_EKPO>-EBELP                "購買伝票の明細番号
        BINARY SEARCH.
    IF SY-SUBRC = 0.
      LDS_DATA-EINDT = LDS_EKET-EINDT.            "明細納入期日
      LDS_DATA-EKET_MENGE = LDS_EKET-EKET_MENGE.  "計画数量
      LDS_DATA-AMENG = LDS_EKET-AMENG.            "前数量 (納入日程行)
      LDS_DATA-WEMNG = LDS_EKET-WEMNG.            "入庫数量
      LDS_DATA-WAMNG = LDS_EKET-WAMNG.            "出庫量
    ENDIF.

    "購買管理における取引先機能より、取引先をITABより取得する
    "購買管理における取引先機能(明細)より取得する
    "購買管理における取引先機能(明細)より取得できない場合は、
    "購買管理における取引先機能(ヘッダ)より取得する
    "該当するレコードがない場合は、取得をパススルーして処理を続行する
    READ TABLE LDT_EKPA_HI INTO DATA(LDS_EKPA_LF_HI)
        WITH KEY
          EBELN = <LFS_EKPO>-EBELN                "購買伝票番号
          EBELP = <LFS_EKPO>-EBELP                "購買伝票の明細番号
          PARVW = 'LF'                            "取引先機能
        BINARY SEARCH.
    IF SY-SUBRC = 0.
      LDS_DATA-EKPA_LF_LIFN2 = LDS_EKPA_LF_HI-LIFN2.
    ELSE.
      READ TABLE LDT_EKPA_H INTO DATA(LDS_EKPA_LF_H)
          WITH KEY
            EBELN = <LFS_EKPO>-EBELN              "購買伝票番号
            PARVW = 'LF'                          "取引先機能
          BINARY SEARCH.
      IF SY-SUBRC = 0.
        LDS_DATA-EKPA_LF_LIFN2 = LDS_EKPA_LF_H-LIFN2.
      ENDIF.
    ENDIF.

    "購買管理における取引先機能より、取引先をITABより取得する
    "購買管理における取引先機能(明細)より取得する
    "購買管理における取引先機能(明細)より取得できない場合は、
    "購買管理における取引先機能(ヘッダ)より取得する
    "該当するレコードがない場合は、取得をパススルーして処理を続行する
    READ TABLE LDT_EKPA_HI INTO DATA(LDS_EKPA_RS_HI)
        WITH KEY
          EBELN = <LFS_EKPO>-EBELN                "購買伝票番号
          EBELP = <LFS_EKPO>-EBELP                "購買伝票の明細番号
          PARVW = 'RS'                            "取引先機能
        BINARY SEARCH.
    IF SY-SUBRC = 0.
      LDS_DATA-EKPA_RS_LIFN2 = LDS_EKPA_RS_HI-LIFN2.
    ELSE.
      READ TABLE LDT_EKPA_H INTO DATA(LDS_EKPA_RS_H)
          WITH KEY
            EBELN = <LFS_EKPO>-EBELN              "購買伝票番号
            PARVW = 'RS'                          "取引先機能
          BINARY SEARCH.
      IF SY-SUBRC = 0.
        LDS_DATA-EKPA_RS_LIFN2 = LDS_EKPA_RS_H-LIFN2.
      ENDIF.
    ENDIF.

    APPEND LDS_DATA TO LDT_DATA.
  ENDLOOP.

  "出力結果をソートする
  SORT LDT_DATA ASCENDING BY
      EBELN                                       "購買伝票番号
      EBELP.                                      "購買伝票の明細番号

  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 = 'EKKO' ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_COUNT3).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 3 TEXT = LDF_COUNT3 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 4 TEXT = 'EKPO' ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_COUNT5).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 5 TEXT = LDF_COUNT5 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 6 TEXT = 'EKET' ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_COUNT7).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 7 TEXT = LDF_COUNT7 ).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 8 TEXT = 'EKPA' ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_COUNT9).
  LDO_HEADER->CREATE_TEXT( ROW = 2 COLUMN = 9 TEXT = LDF_COUNT9 ).
  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.
  • (2022-0107:追記)雑記(こんなと誰も読んでないよね)
    • Code Pushdown初心者(ってなに?w)のころは、何でもかんでもJoin、Inner Join、Left Outer Joinで悩んでいました。10年前にSAPトレーニングHA300(HANA1.0 SPS05)を受講したときに、講師がJoinは遅い、Unionが速いと説明していました。最近になってその意味がようやく分かるようになってきました。SQLロジックを考えるときに、Joinを排除できないか?(実際はそうそう無いのですが)何となく考えるようになりました。MS Accessでも、Joinを排除すると、500万レコード(6GBくらいかな。リンクテーブルを駆使しているので)→200レコードの突合処理が20秒くらいで終わるし。
    • それから他人のコードを見ていると、GROUP BY句を書かないんですよね。GROUP BY句って、ABAP 7.31以前の遥か昔からあるのに。もしかしたら30年前からあったのでは?国内ABAP業界(ってなにw)の見ないふり技術の筆頭「ABAP Objects」以上にGROUP BY句って即効性があって導入ハードルが低いのに。

検索キーワード
ABAP
ABAP 7.31
ABAP 7.4
ABAP 7.50
ABAP 7.51
ABAP 7.52
ABAP 7.53
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
SELECT
JOIN
INNER JOIN
OUTER JOIN
LEFT OUTER JOIN
GROUP BY
MIN()
MAX()
SUM()
ORDER BY
WITH


本当の本題です。さまざまなアーティストに愛されている曲。こういうのを名曲と言うのだと思います。Spotifyをサーフィンしていると幸運にも偶然出逢います。
「What You Won't Do for Love」
最初に発見したのはこれです。私はインストが大好きなのです。ベースカッコいいです。
www.youtube.com
楽器のプレーヤーもカッコいいと感じているようで、すこしYoutubeで調べると所謂「やってみた!」系のカバーが多数出てきます。どれも素敵だと思います。
www.youtube.com
これなんか、何をやっているのか分かり易い。
www.youtube.com
中東でしょうか?アフリカでしょうか?宗教の垣根は無いんだと思います。
www.youtube.com
で、ベーシスト:Victor Wootenのオリジナル曲だとばかり思っていたのですが誤りでした。本家はこちら、AOR界の大御所じゃないですか~。恥ずかしながら灯台下暗しでした。
デジタルシンセ出始めのめちゃくちゃ80年代アレンジですねぇ~。極端なパンとか。(笑)
www.youtube.com
こちら動いている当時のボビーです。ん~セクシー!
っと思っていたらどうしても気になります。ボビーのマイクロホン。なぜSM57?なんでSM58使わないの??僕はボーカリストではなくてプレーヤーなんだって主張したいのでしょうか?
www.youtube.com
こういうの助かります。英語苦手なのです。
www.youtube.com
最近と言っても10年以上前になりますが、ボビーの歌声衰えないですね。
www.youtube.com
同じく大御所ですが、カバー大好きリスペクト指向ピアニストのアレンジですねぇ~。デイビッドですよ。期待を裏切らないです。
www.youtube.com
日本人の大御所もカバーしていますね。
www.youtube.com
イギリスの80年代のアイドルもカバーしていたんですね。
www.youtube.com
グラミー新人賞女性ボーカルもカバーしていますねぇ~。
www.youtube.com
これは驚きでした。ヒップホップ界の大御所もカバーしています。意外と恋だの愛だの軟弱な曲を歌うのね。
www.youtube.com

Spotifyで、「What You Won't Do for Love」を検索すると100曲以上と思いきや、更にスクロール出来て無尽蔵にリストが出てきます。
抜き打ちで聞いてみると、やはりボビーの「What You Won't Do for Love」がアレンジされています。
もしかしたら、ギネスブックに載っているのではと思います。
著作権の期限ってたしか50年と思っていましたが、70年になったんですね。
著作権は永遠に保護されるの? | 著作権って何? | 著作権Q&A | 公益社団法人著作権情報センター CRIC
いったいどんだけ儲けているの?ボビー。もしかしたら著作権のみ保持していて、著作権料を放棄しているのではと疑いたくなります。
誰か知っていたりしないかな~。