SAP 情報系:ユーザ側の本音かも知れない。

  • 転職前は中身にあまり興味を示さない人達が多くて、きちんと実装しても「保守フェーズに入ってからじゃないと評価されないから無駄」とか言われる始末でした。感覚的にはクオリティー(ソースコードの健康!!?)を保つことがローコスト短納期に直結すると思うのですが。
  • 転職しました。急に比較的新しい技術に触れるようになって戸惑いつつ楽しく働いています。実装とリファクタリングを同時並行で進めています。当初会社(というか上司)は、前職と同様に、実装(というか詳細設計)にあまり興味が無いのかと思って、オーバービューレヴェルで軽~く解説していました。しかしながら実際は、興味があるが故の質問や追加要望等たくさん出てきて、設計上の優位性を評価してくれて、とても気持ちが良いです。そんな状況なので何となくですが、手の内を明かすようなCode Pushdown手法のロジックを、今までのようにブログに書いて良いものか、戸惑っています。(止められました。喜んで従います)
  • そんなわけで、ブログ記事としてはつまらないかもしれませんが、今回はコード無しです。

そろそろ本題ということで。既知の常識かも知れませんがユーザさんの本音が少し聞けました。

  • 大前提として私は、SAP BW/4HANAが大好きです。
  • SAP S/4HANAを導入するとSAP BW/4HANAが付属で付いてきます。そのため、情報系で別途プロダクトを準備する稟議はなかなか通らず、SAP BW/4HANAをなんとか使い倒そうということになるそうです。しかしながら、ユーザ様ご担当者レベルでは、使いにくいシステムが出来てしまい最悪は使い物にならないのではとの、懸念を抱くそうです。
  • おそらくですが、世の中のSAP BWシステムは以下の問題点を抱えながら運用しているのだと思います。
    • パフォーマンスが悪い。導入時に導入ベンダにパフォーマンス改善を要望し続けるのですが、様々な説明を受けて時間切れとなってしまう。
    • BWの仕組みとして、BW変換、BWクエリ、OLAP変数(Customer Exit)が強調して動作するので、ロジック配置が分散してしまう傾向にあり、保守性を著しく低下させてしまう。
  • SAP BW/4HANAの利点は私は詳しくないのですが(って言ったら会社に怒られるかな!!?)、定義済みのビジネスコンテンツを利用することで、インターフェイスアドオン開発が削減出来て、導入コストが抑えられると説明しているのだと思います。
  • ビジネスコンテンツの実態って、例えば総勘定元帳(BKPF & ACDOCA)だとデルタで明細レベルデータを引っ張る動きをします。一方で、SAP BW/4HANAでは月単位のレポートが主で、明細データを参照するケースってあまり無いのではと思います。そのため、ほとんどのプロジェクトでは、SAP BW/4HANA内でSAP BWレガシーな手法であるレコードセットベース(つまりループ処理)のBW変換を使って、加工済みデータのコピーを多数作成することになります。
  • 上記手法は以下の問題点があり、ほとんどの場合最後まで解決しないままユーザ様があきらめてカットオーバーを迎えると思います。
    • BW変換はループ処理なのでパフォーマンスを確保するのが難しく、機能要件の一部を妥協して夜間バッチ処理がギリギリ終わるか、終わらないか。
    • 伝言ゲームのように、加工済みデータのコピーを多数作るので、データリカバリ要件が複雑になり、運用の工数が増大し、難易度が高くなってしまう。
    • BW変換を動かすには、DTPやBW変数等様々なオブジェクトが必要なため、ロジックが分散する傾向にあり、成果物の可読性が著しく低下し影響範囲の特定を難しくしてしまう。そのため保守性が悪く、変化に弱い仕組みとなってしまう。
    • 上記(パフォーマンス確保、ロジックの分散)に付帯して、データ加工が中途半端(途中であきらめてしまう)になる傾向にあり、無理やり機能要件に合わせるために、BWクエリの実装が肥大化して、ますますパフォーマンス確保が難しくなり、ロジックが発散してしまう。
  • ではどうしたら良いのか?、私の考える解決方法は、ビジネスコンテンツは最小限に、SAP S/4HANAにABAP CDS ViewやHANA Information View等で、加工集計済みデータをSAP BW/4HANAにデルタ連携する。(ここが私の強みなので、やれるものならやってみろとの思いもありますが(土井善治風に..))。って、他社の分析系の仕組を導入するときも同じような設計をしているような気がします。(違うのかな!!?わかんないや)
  • ハードルは以下2点。
    • 明細情報はSAP S/4HANAで参照するように、ユーザ様を説得する。
    • ビジネスコンテンツでインターフェイスアドオン工数削減するとアナウンスしないこと。

若干話が飛びます。

  • SAP Data Warehouse Cloudなんていう製品もあるようですが、私は中身について一切知りません。そのうえでいい加減なことを書きます。(勉強したら考えが変わるかもしれませんが、と言うか勉強したいです)
  • SAP BW/4HANAは廃止して、SAP S/4HANA Include Enbeded BWに統合して、インターフェイスアドオン不要のシンプルな構成にするべきと思っています。(ドイツ人はどのように捉えているのは分かりませんが)
  • そんなわけで、SAP S/4HANAのEnbeded BWネタです。
  • SAP BW/4HANAのCode Pushdownといえば以下の印象です。
    • HCPR(複合プロバイダ)ですよね?多分SAPJもそのように説明しているのではと思う。
    • HCPRって、SAP BW on HANAのBW CUBEの時ように、へたっぴいにHANA Information Viewをラッピングしている。
    • HCPRで本気にCode Pushdownに取り組もうとすると自由度が低すぎて破綻する。
    • HANA Information Viewを習熟している身としては、歯がゆい。
    • HCPRで試行錯誤するよりも、HANA Information View習得コストの方が少なく済むと思う。
  • 上記歯がゆさ解消ということで、
    • HANA Information View(ABAP CDS Viewでも良い)に業務ロジックのほとんどを実装する。
    • 上記HANA Information ViewをOpen ODS Viewで薄くラッピングして、BW層に持ってくる。
    • HCPR、BWクエリは、原則、階層処理、時間依存処理のみの実装とする。
    • つまり、HCPR、BWクエリには業務ロジックを実装しない。
  • Open ODS Viewで、各項目にInfo Objectを割り当てるのですが、ここで大きな発見がありました。自分で作らなくて良いYo!!
    • Info Objectの技術名称プレフィックス「/ERP」を検索すると大概の項目は予めSAPが用意してある。
    • 特筆すべきは、Info Object:0MANDTの動き!!!! SY-MANDTを導出するCustomer ExitもSAP標準で用意しているので、BWクエリにこいつを放り込めばAll OK!!!!!
  • そんなわけで、Enbeded BWに、HANA Information View、SAP標準Info Object、AnalysisOfficeで、バタバタと会計情報のリアルタイムレポートを作成しました。もちろんクライアント制御も効いています。あとは、勘定グループを割り当てる程度で良い感じになりそうです。

またまた話が飛びます。

  • 転職先は、SAP導入ベンダ以前にクラウドベンダを名乗っています。AWS、Azure、GCPがメインの事業領域だったりします。
  • 今までは、Code Pushdownでパフォーマンス確保に奔走していました。パフォーマンスは正義だ!(草)
  • 今でも、パフォーマンスは正義なのですが、クラウドといえば処理を軽くすると経費節減ですね。
  • 自身の中で、パフォーマンスを確保する処理方法と同時に、処理量そのものを削減するにはどうするか?そんなことを考えるようになりました。場合によっては加工済みデータのコピーを持つのも悪くないかと。

最近の神様からの贈り物について

  • 楽しんで働いていると、私に神様がたくさん語り掛けてくれるようです。いろいろ思考錯誤はありましたが、神様から贈り物、しっかり受け止めました。
  • 過去月次の在庫を出力する要件を、Viewロジックのみで実現しました。もちろん在庫移動の無い月も正しく在庫を出力します。

最近の悩み事について

  • SAP HANAって、組み込み関数群があるんですよね。3種類あって、一部は自作可能なんです。
    • BFL:割引率計算とか組み込み関数で出来るようです。
    • AFL:関数自作プラットフォーム:Cで記述するのかな?:私一応C書けます。基本情報もCで受けたし。
    • Enbeded R(SAP HANAネイティブで動くそうです):最近はPythonと一緒に組み合わせるとかで、流行っていますよね。
  • 上記で何ができるのかよく分からないのと、エンタープライズ領域の要件で適用方法、アイデアの、具体的は手法が分からないのです。
  • まずは何ができるのか把握しないと、始まらないんでしょう。
  • 少しずつですが、RStudioで、Rを勉強中です。


最近、中田ヤスタカの2000年代を聴いてます。エッジが効いていて心地よいです。中田ヤスタカの動画や画像を見ていると、良い意味でオタクだなぁ〜って思います。例えば、スティージョブズが復帰する直前の、ピザボックスの頃のAppleとか。外付けMOドライブとか。我らがアラフィフのオタク心をくすぐられます。乱暴な話ですが、おじさん達は全員、中田ヤスタカが好きだからって思います。小室哲哉をリスペクトしているのが、楽曲のあらゆるところから滲み出ていると感じます。

Windows11を古いPCにインストールしてみました。

本題のWindows11のインストールです。

  • Windows10が動けばいいやって感じで、第2世代Core プロセッサーファミリー(Sandy Bridge)、第3世代Core プロセッサーファミリー(IvyBridge)のPCを今だに何台か使っています。当然Windows11は動きませんよということでまだ使えるけれど最新のOSが動かないのであればついに買い替えかなって思っていました。
  • 諦めていた日々が約半年、久しぶりにWindows11インストールのことを調べてみると、以下を満たせば動いているところを見ることが出来るそうです。
    • CPUがEM64T(って最近言うのかな!?)に対応
    • 1GB以上のメモリ
  • このスペックってさ~Windows XP x64 Editionのころのスペックだよね。って古すぎ!??
  • YouTubeで調べるとすぐに出てくるのですが以下の手順でインストール出来ました。いまWindows11で記事を書いています。
    • Windwos11 isoイメージをダウンロードする。ググれば直ぐにMSサイトが出てくる。マイクロソフトアカウントでログオンが必要なのかな?
    • isoイメージのファイルを展開する。
    • 展開したファイルから、「appraiserres.dll」を検索して削除する。
    • 念のためPC再起動する。←ここは私のオリジナルの手順です。
    • ネットワークを遮断する。(例えば機内モードとかにする)
    • setup.exeを実行して、道なりに進める。
    • Windows11が動いている。Windows Updateも普通に動いている。
  • あまりにも簡単で拍子抜けしました。自宅にあるPC3台が数時間でWindows11になりました。レスポンスはWindows10と変わらないです。ストップウォッチで測れば変わるのかもしれませんが。
  • 今回は、第2世代Core プロセッサーファミリー(Sandy Bridge)でもWindows11が動作することくらいかな。どこにでもある情報のみでごめんなさい。いろいろ嬉しかったので勢いで書いちゃった。

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、一発回答、ドン引きレヴェルの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:ソースコード一部修正:結果セットを表示する場合はチェックボックスにチェックを付けて下さい。

  • α:渾身のSQL、一発回答、ドン引きレヴェルのCode Pushdown
*&---------------------------------------------------------------------*
*& Report YSQL_CONSOLE_ALPHA
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT YSQL_CONSOLE_ALPHA.

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

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

PARAMETERS P_ISDISP AS CHECKBOX DEFAULT ABAP_FALSE.

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.
  PERFORM START_OF_SELECTION
    USING
      P_ISDISP
      S_EBELN[]
      S_EBELP[]
      S_MATNR[]
      S_WERKS[]
      S_LGORT[]
    CHANGING
      GDS_BAPIRET2
      GDF_SUBRC.
  IF GDF_SUBRC <> 0.
    LEAVE TO LIST-PROCESSING.
  ENDIF.

*-----------------------------------------------------------------------
* SUBROUTINE
*-----------------------------------------------------------------------
FORM START_OF_SELECTION
    USING
      PIF_ISDISP TYPE CHECKBOX
      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( ).

  DATA(LDO_H_LABEL) = LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 ).
  LDO_H_LABEL->SET_TEXT( 'Performance data' ).

  DATA(LDO_H_FLOW) = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Input records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKKO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 3 ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_EKKO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKKO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 4 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 5 ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_EKPO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 6 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKET' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 7 ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_EKET_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKET_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 8 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPA' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 9 ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_EKPA_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Output records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_DATA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Responce Time [sec]' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( 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_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.
  • β:折衷案その一、混乱の元「FOR ALL ENTRIES IN」を排除、内部テーブルのバケツリレーも排除
*&---------------------------------------------------------------------*
*& Report YSQL_CONSOLE_BETA
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT YSQL_CONSOLE_BETA.

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

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

PARAMETERS P_ISDISP AS CHECKBOX DEFAULT ABAP_FALSE.

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.
  PERFORM START_OF_SELECTION
    USING
      P_ISDISP
      S_EBELN[]
      S_EBELP[]
      S_MATNR[]
      S_WERKS[]
      S_LGORT[]
    CHANGING
      GDS_BAPIRET2
      GDF_SUBRC.
  IF GDF_SUBRC <> 0.
    LEAVE TO LIST-PROCESSING.
  ENDIF.

*-----------------------------------------------------------------------
* SUBROUTINE
*-----------------------------------------------------------------------
FORM START_OF_SELECTION
    USING
      PIF_ISDISP TYPE CHECKBOX
      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( ).

  DATA(LDO_H_LABEL) = LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 ).
  LDO_H_LABEL->SET_TEXT( 'Performance data' ).

  DATA(LDO_H_FLOW) = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Input records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKKO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 3 ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_EKKO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKKO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 4 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 5 ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_EKPO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 6 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKET' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 7 ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_EKET_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKET_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 8 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPA' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 9 ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_EKPA_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Output records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_DATA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Responce Time [sec]' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( 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_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.
  • γ:折衷案その二、折衷案その一と同一ロジックで「FOR ALL ENTRIES IN」を使用。
*&---------------------------------------------------------------------*
*& Report YSQL_CONSOLE_GAMMA
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT YSQL_CONSOLE_GAMMA.

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

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

PARAMETERS P_ISDISP AS CHECKBOX DEFAULT ABAP_FALSE.

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.
  PERFORM START_OF_SELECTION
    USING
      P_ISDISP
      S_EBELN[]
      S_EBELP[]
      S_MATNR[]
      S_WERKS[]
      S_LGORT[]
    CHANGING
      GDS_BAPIRET2
      GDF_SUBRC.
  IF GDF_SUBRC <> 0.
    LEAVE TO LIST-PROCESSING.
  ENDIF.

*-----------------------------------------------------------------------
* SUBROUTINE
*-----------------------------------------------------------------------
FORM START_OF_SELECTION
    USING
      PIF_ISDISP TYPE CHECKBOX
      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( ).

  DATA(LDO_H_LABEL) = LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 ).
  LDO_H_LABEL->SET_TEXT( 'Performance data' ).

  DATA(LDO_H_FLOW) = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Input records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKKO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 3 ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_EKKO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKKO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 4 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 5 ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_EKPO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 6 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKET' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 7 ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_EKET_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKET_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 8 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPA' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 9 ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_EKPA_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Output records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_DATA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Responce Time [sec]' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( 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_SELECTIONS( )->SET_SELECTION_MODE( VALUE = IF_SALV_C_SELECTION_MODE=>CELL ).
  LDO_ALV->DISPLAY( ).

  POF_SUBRC = 0.
ENDFORM.
  • λ:レガシー実装のイメージ。ITABのバケツリレーも再現。
*&---------------------------------------------------------------------*
*& Report YSQL_CONSOLE_DELTA
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT YSQL_CONSOLE_DELTA.

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

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

PARAMETERS P_ISDISP AS CHECKBOX DEFAULT ABAP_FALSE.

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.
  PERFORM START_OF_SELECTION
    USING
      P_ISDISP
      S_EBELN[]
      S_EBELP[]
      S_MATNR[]
      S_WERKS[]
      S_LGORT[]
    CHANGING
      GDS_BAPIRET2
      GDF_SUBRC.
  IF GDF_SUBRC <> 0.
    LEAVE TO LIST-PROCESSING.
  ENDIF.

*-----------------------------------------------------------------------
* SUBROUTINE
*-----------------------------------------------------------------------
FORM START_OF_SELECTION
    USING
      PIF_ISDISP TYPE CHECKBOX
      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( ).

  DATA(LDO_H_LABEL) = LDO_HEADER->CREATE_LABEL( ROW = 1 COLUMN = 1 ).
  LDO_H_LABEL->SET_TEXT( 'Performance data' ).

  DATA(LDO_H_FLOW) = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Input records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKKO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 3 ).
  SELECT FROM EKKO FIELDS COUNT(*) INTO @DATA(LDF_EKKO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKKO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 4 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPO' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 5 ).
  SELECT FROM EKPO FIELDS COUNT(*) INTO @DATA(LDF_EKPO_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPO_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 6 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKET' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 7 ).
  SELECT FROM EKET FIELDS COUNT(*) INTO @DATA(LDF_EKET_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKET_COUNT ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 8 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'EKPA' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 2  COLUMN = 9 ).
  SELECT FROM EKPA FIELDS COUNT(*) INTO @DATA(LDF_EKPA_COUNT).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_EKPA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Output records' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 3  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = LDF_DATA_COUNT ).

  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 1 ).
  LDO_H_FLOW->CREATE_TEXT( TEXT = 'Responce Time [sec]' ).
  LDO_H_FLOW = LDO_HEADER->CREATE_FLOW( ROW = 4  COLUMN = 2 ).
  LDO_H_FLOW->CREATE_TEXT( 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_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
いったいどんだけ儲けているの?ボビー。もしかしたら著作権のみ保持していて、著作権料を放棄しているのではと疑いたくなります。
誰か知っていたりしないかな~。

ヘッドホン:SENNHEISER HD600 番台:(2) エージング

  • 急に病気が重症になって、HD580 Precision、HD650GE、HD660sを立て続けに入手して、比較なんかしてみようと思ったとですが、HD660sのエージングがあまりにもドラスティックに変化したので、独立して記事にしました。

f:id:dai_h_73:20211023210136j:plain
f:id:dai_h_73:20211023210253j:plain

  • SENNHEISER HD660s は、最初の所有者は30時間鳴らして手放して、2番目の所有者は15時間鳴らして手放したとのことで、中古を入手しました。
  • 最初の印象は、こもっていて、特に高域の歯切れが悪くて、万が一もしかしたら偽物と思う程酷い音に聞こえました。これなら手放しても仕方がないよなって思いました。入手前にヨドバシ本店で鳴らした時も同じ傾向で、本当にリファレンスクラスなのか疑問に思いました。この感覚ってMDR-M1stにとても似ていました。前評判は良いのに、鳴らしてみるとヒドイという感覚が誤魔化しきれませんでした。
  • その後、いろいろ調べるとHD660sはエージングが必要とのこと。
  • それならば、きちんとエージングしてみようと思い、インターネットを色々調べました。SENNHEISER HD660sのエージング目安は200時間くらい必要だそうです。ヘッドホンのエージングサイトというのがあるそうで、今回はこれを利用しました。エージングサイトを利用すると、10時間分が1時間で完了するそうです。そのため、エージング200時間分実施する場合、エージングサイトでは20時間(20回)実施することになります。
  • 以下、エージング内容です。都合50時間分になるのでしょうか?
    • SENNHEISER HD660sはオープンエアー型なので、音漏れが多いです。そのため、リュックの中に入れて、エージング サイトで1時間エージングしました。
    • 更に、リュックの中に入れて、エージング サイトで1時間エージングしました。
    • 音楽を最大音量で30時間鳴らしました。

f:id:dai_h_73:20211023210514j:plain
f:id:dai_h_73:20211023210427j:plain

  • エージング50時間の感想として、音の抜けと言ったらいいのでしょうか、こもった感じがかなり改善したと思います。ヘッドホンに関してエージングは不要で意味無しとの説もありますが、SENNHEISER HD660sに関しては、体感できるのではないかと思いました。

キーワード:
SENNHEISER
HD580 Precision
HD600
HD650
HD650GE
HD660s
Golden Era
エージング
エージングサイト
MDR-M1st

ヘッドホン:SENNHEISER HD600 番台:(1) 散財中


f:id:dai_h_73:20210928123704j:plain

  • 以前下調べをしました。ヘッドホン:SENNHEISER HD600 番台:(0) 購入検討:(評論家について思う事) - dai_h_73’s diaryそして、オークション等で入手した機種は以下の通りです。なお、あらかじめ病気が重症になることを見越して、中華製のイヤーパッドを確保して、これを普段使いとして使用することにしました。そのため、後日比較なんかしようと思っていますが、各機種の裸特性が比較出来ると思います。
    • SENNHEISER HD580 Precision
      • 中古を落札:消耗品を交換したので状態は良いです。
      • バッフルのメッシュ素材が黒の貴重品でした。音質的に最も好みでした。
      • f:id:dai_h_73:20210928123748j:plain
      • f:id:dai_h_73:20210928123830j:plain
    • SENNHEISER HD650:ジャンクを落札:以前は音出しOKとのことでしたが、左のドライバのボイスコイルが駄目でした。そのため部品どり決定。(涙)
      • HD650ジャンクですが、悪臭がひどく埃と油まみれで掃除が大変でした。
      • お金はかかってしまいましたが、掃除の過程で分解方法をインターネット検索して、分解技術と度胸を手に入れました。
        www.youtube.com
      • 同シリーズと長く付き合っていく勇気と覚悟も同時に手に入れました。草
      • こちらのシリアルは、0X~台でした。後述のGolden Era以前のモデルのようだったので、ドライバ破損は非常にもったいなく思います。
      • とりあえず適当な中華ドライバに入れ替えて遊んでます。
    • SENNHEISER HD650:中古を落札:シリアルを質問したら教えて頂けて、あっさり落札出来ました。
      • ワンオーナー品で、シリアル番号がビンゴだったので、ほぼ間違いなくGolden Eraと思います。2個目で入手出来てラッキーだったと思います。
      • こちらも手際よく分解し、食器用洗剤、歯ブラシ、アルコール等を駆使して清掃いたしました。
      • ドライバの端子の接触不良がありました。上記の分解動画で紹介していましたが、ドライバユニットの極小スプリングを修正しました。
      • 振動板の中心が(左右両方)つぶれて(押し込まれて)いました。少し勇気がいりましたが、ユニットの裏側から「ぷっ」っと息を吹きかけて修復しました。「ふ~」ではなく「ぷっ」が良いようです。さらにノウハウと度胸がつきました。草
      • オリジナルを尊重したいので、Dmaaチューニングは行わない予定です。(既に出来ないし)
      • f:id:dai_h_73:20210928124036j:plain
      • f:id:dai_h_73:20210928124110j:plain
    • SENNHEISER HD660s:中古を落札:年式も新しいし人気があるのか最も高価でした。
      • 外装に一部難ありでしたが、修復出来ました。音質的にはもっとも印象に残らない感じでした。中古とはいえエージング!!?が終わっていないのかも。
      • 同機種がモダンな音質とのことなので、じっくり聞いて体(耳)をエージングしていこうと思います。草
      • f:id:dai_h_73:20210928124202j:plain
      • f:id:dai_h_73:20210928124234j:plain
    • SENNHEISER HD600(未入手):お店で試聴したし、HD580 Precisionがあるので何となく音質の傾向が予測付きます。気長に探そうと思います。(シリアルよりGolden Eraと思われる個体を入手しました)
    • SENNHEISER HD580 Jubilee(未入手):HD580 Precisionの網をHD650ジャンクの網に付け替えると再現できるかな?草
  • SENNHEISER HD600 番台といえば、ハイインピーダンス(300Ω)が主流なことに加えて、オープンエアのため能率が比較的低めです。そのため、音量を確保する目的でヘッドホンアンプということになります。しかしながら、「ヘッドホンアンプからコードが伸びて音楽を聴く」ワイヤレスの便利さを知ってしまったので、ワイヤードのままにしておくと稼働率が下がってしまう。
  • 世間様はヘッドホンアンプとおっしゃいますが、音質がとても素敵なので稼働率を上げるため、音質を犠牲にしてもたくさん聴いていたいと思いました。そのためBluetoothレシーバです。音量的に心配でしたが大丈夫でした。
  • いろいろなネットの書き込みを読んでいると、「ハイインピーダンス→駆動力が必要」との記述を見ますが、ほんの少しでも電気回路をかじった経験があれば誤りであることに気づくと思います。音量の確保の問題は残りますが、アンプの負担という事であればローインピーダンスの方が負担が大きいのです。
  • その証拠に、一般的なオーディオアンプのスペック表には、4-16Ωとか書かれていたりして、16Ω以上を接続してもオーディオアンプは壊れませんが、4Ω以下を接続すると限りなくショートに近づくのでオーディオアンプが壊れます。
  • ハイインピーダンスのヘッドホンは、アンプに優しいけれど音量の確保が難しく、音量の確保が出来ればとりあえず問題なしと思うのです。スペック的にも同一のアンプであればハイインピーダンスの方がダンピングファクタが高くなるので、世間で言われているほど気にしなくて良いかと思うのです。もちろん別途ヘッドホンアンプを使用すれば、使用しない場合と比較して音質は良くなると思いますが、ヘッドホンの機種を超えるほどの改善(変化)は無いと思います。
  • そもそもヘッドホンアンプにお金を掛けるのであれば、その分を追加してHD800系を購入した方が幸せになれるように思います。
  • いろいろ書きましたが、普段は並みのBluetoothレシーバーで聴いて、ちょっと真面目に聞くときはES100 MK2 Bluetooth Receiver – Earstudioでバランス駆動すれば良いかなって思います。
  • 一番心配していたHD580 Precisionですが、きちんと音量確保できました。AKG K701よりも気持ち音量を上げる程度でした。アンプのボリュームって出力電圧とリニアだからね。
  • SENNHEISER HD600 番台をぱっと聞いたところ、どの機種も共通して感じたのですが、キャパシティが大きい、つまり音量を上げてもやかましくならないです。TDLのパレードBGMが比較的大音量だと思うのですが、やかましくならないので赤ちゃんが泣きださない感じといったところでしょうか。裏を返すと、ぜいたくな悩みなのですが、適正な音量をどこにするのか迷います。草
  • HD650が低音寄りとの評判ですが、思ったよりもスッキリしていたのが意外でした。お店の試聴ではHD600(HD580)が好印象だったのですが、家で数時間聴いてみるとHD650が好印象になりました。最も分解清掃を念入りに行ったので愛着バイアスがあるかもしれません。
  • HD660sが評判ほどいいと思えませんでした。有名なレビューアの記事Sandal Audio: ゼンハイザーHD660Sの試聴レビューにも書かれていたので、たっぷりエージングしようと思います。
  • じっくり聞き込んだら音質比較なんかをしてみようと思います。

キーワード:
SENNHEISER
HD580
HD580 Precision
HD580 Jubilee
HD600
HD650
HD660s
Golden Era
Dmaa
ES100
グランド分離
バランス駆動
AKG K701
オープンエア

オーディオ:音場補正(3):A-7vl、88.2kHzで動くじゃん!!

f:id:dai_h_73:20210911192603j:plain

  • そんなこともあり、おこずかいそんなに無いし、SRC2496購入のモチベーションが著しく低下していました。新品を購入するのが手っ取り早いのですが、オークションで狙うこと1年、手ごろな価格で落札できました。(1万円を大きく切りました)
  • 動作確認もそこそこ、AES/EBUケーブルを入手したり、1ヶ月近く放置していて、本日セッティングしてみました。写真の通りなのですが、音が出ました!!

f:id:dai_h_73:20210911192658j:plain
f:id:dai_h_73:20210911192805j:plain

  • 音の違いはあるのかどうか?正直分からないけれど、演算誤差によるビット落ち等が最小限に抑えられているはずなので、精神衛生上気分が良いです。
  • A-7vlは以下すべての組合せで動作しました。
    • サンプリング周波数:32kHz、44.1kHz、48kHz、88.2kHz、96kHz
    • ビット深度:16bit、20bit、24bit

キーワード:
BEHRINGER
SRC2496
DEQ2496
A-7vl
AES/EBU
アップサンプリング
整数倍
演算誤差
44.1kHz
16bit
88.2kHz
24bit
音場補正

ヘッドホン:MDR-Z1000:ヘッドバンド問題

  • 自身の価値観として外見より中身を重視する。少なくとも自己認識としてはそうなんです。
  • ヘッドホンを多数所有すると、PUレザーの加水分解による劣化が避けられません。なかなか悩ましいですよね。ヘッドホンは肌に触れるので体から放出される水分がイヤーパッド、ヘッドバンドに付着するのが避けられないのです。特に夏は汗が多量に付くので気がついたら乾拭きするようにしてます。しかしながら中古の叩き売りを入手するとはじめからボロボロなんです。
  • そんなわけでいろいろ考えました。外見に犠牲になってもらい、音質に影響無く、ボロボロのカスとも向き合わなくて良く、簡便で安価な方法を発見しました。文書にすると大げさですね。
    • 考え方として、ボロボロのカスが出るのであれば、予めカスの原因を全て取り除けば良いのです。PUレザーをガムテープ等で完全に剥がしてしまうのです。
  • やってみたのがこれです。
    • 一部施術済み
    • f:id:dai_h_73:20210804203824j:plain
    • 完全施術済み:実は同機種を2台所有しています。2台目はヤフオクで実質\4,000位で落札しました。出品者が無知なのかジャンク多量売りに紛れ込んでいました。
    • f:id:dai_h_73:20210804203711j:plain
    • かっこ悪い?音質に影響は無いです。もちろんカスは出ません。スキンヘッドの人がフケと無縁なのと同じですね。当たり前ですが他機種にも適用可能です。
  • 普段は別に気にならないのですが、電気屋さんのヘッドホンコーナーへ行くのだけ、気が引けます。

キーワード:
ヘッドバンド
ヘッドバンド交換
ヘッドバンド劣化
ヘッドバンド加水分解
ヘッドバンドのカス
イヤーパッド
中華イヤーパッド
互換イヤーパッド
MDR-Z1000
MDR-Z900HD
MDR-Z900
MDR-Z600
MDR-CD900
MDR-CD900st
MDR-7506
AKG K550