ABAPソースコードの読み方|実務で迷わない調査の順番とコード例

ABAPソースコードの読み方。入力データから抽出・分岐・結果へ進む流れと、コードを調べる虫眼鏡のイラスト SAPテック

「ABAPの文法は少し分かる。でも、長いソースを開くと何から読めばいいのか分からない」。保守や問い合わせ調査では、こうした壁に当たりやすくなります。

実務でまず答えたいのは、どの入力で、どのデータを取り、どこで結果が変わったかです。今回は「出るはずの明細が一覧に出ない」という架空の問い合わせを使い、ソースを読む順番を具体的に整理します。

対象:ECCやSAP S/4HANAのオンプレミス/Private Editionなどで使われる、クラシックABAPの実行可能レポートです。コード例はABAP 7.40 SP08以降を想定しています。製品名だけでなく、実際のABAPリリースとSPも確認してください。

ABAP CloudやRAPでは、利用できるAPIや処理の入口が異なります。後半で違いに触れますが、このレポートの構造をそのまま当てはめるものではありません。コード・データ・問い合わせはすべて説明用に作った架空の例です。

ソースを開く前に、調査の問いを一文にする

「このプログラムを理解する」だけでは、調べる範囲が広すぎます。たとえば、次のように対象と期待結果を固定します。

調査する問い:会社コード1000・通貨JPY・最低金額10,000の条件で実行したとき、明細D003が一覧に出ないのは、どの条件で除外されるためか。

  • システム・クライアント、実行日時、実行ユーザーを確認する。
  • 起動したTコードやジョブ、使用したプログラム・バリアントを確認する。
  • 入力値、対象伝票と明細キー、期待結果と実際の結果をそろえる。
  • 同じ条件で正常になる1件も、比較用に用意する。

正常データとの差を比べられると、すべての分岐を追わずに済みます。本番と検証で動きが違う場合は、コードだけでなく、データ・設定・権限・有効なバージョンの違いも候補に残します。

最初に「本当に動いている入口」を見つける

ABAPは、ソースファイルの先頭からすべての行が順番に実行されるわけではありません。宣言、イベントブロック、メソッド、サブルーチンを分けて見ます。

実行の入口最初に確認すること
Tコードから実行SE93でトランザクション種別と呼出先を確認。パラメータ/バリアントトランザクションでは委譲先も追う。
バックグラウンドジョブSM37の対象実行とジョブステップから、プログラム・バリアント・実行ユーザーを確認。
エラー・ダンプメッセージのクラス/番号、ST22の該当実行と呼出し位置などから絞る。
メソッドや関数から開始利用先検索で候補を探し、実際の呼出元は実行時のコールスタックで確認。動的呼出しには注意。

実行可能レポートでは、PARAMETERSは選択画面の定義です。入力チェックなどの選択画面処理を経て、START-OF-SELECTIONの処理に進みます。SAP公式:START-OF-SELECTION

INCLUDEは、それ自体が独立した実行入口とは限りません。メインプログラムに組み込まれたコードとして、所属する処理ブロックを確認します。Tコードの用途を調べたい場合は、SAP開発でよく使うトランザクションコード一覧も参考にしてください。

短いコードで、入力から結果までを追ってみる

次は、未処理明細を取得し、ブロックされた明細と最低金額未満の明細を除外する例です。実務コードの全機能を再現するものではなく、読解に必要な流れだけを残しています。

zt_read_demoは架空のDDICテーブルです。項目は会社コードbukrs、伝票doc_id、明細item_no、金額amount、通貨waers、状態status、ブロックblockedを想定します。1行は伝票明細1件で、status = 'O'を未処理、blocked = 'X'をブロックありと、この例の中だけで定義しています。

テーブル定義、権限チェック、業務上の金額・通貨処理、エラー処理などは省略しています。そのまま実行できる完成プログラムではなく、実機でのコンパイル・動作確認も行っていません。金額の例は、すべて同一通貨のJPYとして読んでください。ここではamountとp_minが同じ尺度の数値を持つものとし、通貨桁数に応じた表示・換算は扱いません。

REPORT zread_flow_demo.

PARAMETERS:
  p_bukrs TYPE zt_read_demo-bukrs OBLIGATORY,
  p_waers TYPE zt_read_demo-waers OBLIGATORY,
  p_min   TYPE zt_read_demo-amount.

START-OF-SELECTION.
  SELECT doc_id, item_no, amount, waers, blocked
    FROM zt_read_demo
    WHERE bukrs  = @p_bukrs
      AND waers  = @p_waers
      AND status = 'O'
    INTO TABLE @DATA(lt_items).

  DATA lt_result LIKE lt_items.

  LOOP AT lt_items INTO DATA(ls_item).
    IF ls_item-blocked = 'X'.
      CONTINUE.
    ENDIF.

    IF ls_item-amount < p_min.
      CONTINUE.
    ENDIF.

    APPEND ls_item TO lt_result.
  ENDLOOP.

  WRITE: / 'Result count:', lines( lt_result ).

実際の一覧表示は省略し、最後は件数だけを出しています。ALVなどの一覧表示では、結果テーブルを渡した後のフィルタやレイアウト設定も確認対象です。

この例のSELECT ... INTO TABLE @DATA(...)は、取得結果を内部テーブルへまとめて受け取る書き方です。SELECT ... ENDSELECTの行単位のループとは区別します。SQLのINTOでのインライン宣言は7.40 SP08からの機能です。SAP公式解説:ABAP News for 7.40, SP08 — Open SQL

最初の一周は、五つの塊だけ読む

  • 入力:p_bukrs、p_waers、p_minが、実行時に何になっているか。
  • 取得:SELECTのFROMとWHEREで、何をどの条件で取るか。
  • 加工:LOOPの中で、どの行を落とすか。
  • 結果作成:APPENDまで到達した行が、どこへ入るか。
  • 出力:最後に使われるテーブルが、どれか。

この段階では、変数宣言を全部覚える必要はありません。lt_itemsとlt_resultを見分け、DBから取れなかったのか、取得後に落ちたのかを切り分けます。

入力を会社1000・JPY・最低金額10,000とし、SELECTで次の3行が取れたと仮定します。伝票番号は説明用で、各行の明細番号は000010です。SELECTにORDER BYはなく、取得順は指定していません。表は説明のため伝票番号順に並べています。

架空の明細取得した値このコードでの結果
D00115,000 JPY
ブロックなし
APPENDに到達する
D0025,000 JPY
ブロックなし
金額条件でCONTINUE
D00320,000 JPY
ブロックあり
ブロック条件でCONTINUE
会社1000・JPY・最低金額10000でSELECTした3明細のうち、D002は金額不足、D003はブロックで除外され、D001だけが結果に残る流れ
SAP TRIP作成の概念図。架空のデータであり、実際のSAP画面ではありません。

D003が出ない直接の理由は、最初のIFでブロックありと判定されることです。コードどおりに除外されることと、その業務条件が正しいことは別です。「ブロック明細も出すべきか」は、仕様や設定の担当者に確認します。

SELECTは、取得元・条件・粒度・受け皿の順に読む

SELECTを見たら、まず「1行が何を表すか」を確認します。ヘッダー1件なのか、伝票明細1件なのかで、想定件数は変わります。

  • FROM:テーブル、ビュー、CDSなど、実際の取得元はどれか。
  • WHERE:入力項目だけでなく、固定値や範囲条件が入っていないか。今回ならstatus = 'O'でも絞っている。
  • JOIN:相手に一致しない行を落としていないか。1対多の結合で行が増えていないか。
  • 項目とキー:後で判定・照合に使う項目が取得されているか。キーの一部だけで照合していないか。
  • INTO:どの変数・内部テーブルに入り、後続で何を使うか。

今回の@p_bukrsの@は、ABAP側のホスト変数を示します。WHEREに書かれた変数名を見るだけでなく、実行時の値まで確かめるのがポイントです。

実データには対象があるのにSELECT後のテーブルにないなら、会社・通貨・状態・結合条件などを照合します。クライアント依存テーブルでは、実行セッションのクライアントと、ABAP SQLの暗黙のクライアント処理も忘れずに確認します。

遅い処理でも、ソースの見た目だけでSQLが原因と断定しません。許可された環境でST05などのSQLトレースを使い、実際に実行されたSQL、回数、取得件数、所要時間を確認してから絞ります。SAP公式:SQL Trace Analysis

内部テーブルは「入った・消えた・変わった」を追う

名前のlt_やls_は、よく使われる命名の慣習で、言語の決まりではありません。型宣言やADTの型情報で、内部テーブルなのか1行分の構造なのかを確かめます。

今回のLOOP AT ... INTO ls_itemは、1行分を作業領域へ読み込む形です。ls_itemを書き換えただけでは元の内部テーブルの行は更新されません。ASSIGNINGでフィールドシンボル経由の行を扱う場合とは、変更先が異なります。

途中で件数や値が変わるなら、対象テーブル名に加えてAPPEND、INSERT、MODIFY、DELETE、CLEAR、FREEを探します。単純な代入、CORRESPONDING、参照やメソッド引数経由の変更も候補です。文ごとにDB操作か内部テーブル操作かを読み分けます。

分岐では「条件」と「どこを抜けるか」をセットで読む

IFの条件だけ追って、CONTINUEやRETURNを読み飛ばすと、結果の理解がずれます。特にCHECKとEXITは、ループの内側か外側かで意味が変わります。

文読むときの要点
CONTINUE現在のループの、その周回の残りを飛ばす。次の周回へ進む。
CHECK 条件条件が偽なら、ループ内ではその周回を終了。ループ外では現在の処理ブロックを終了。
EXITループ内では、そのループを終了。入れ子の外側まで一度に抜けるわけではない。ループ外では処理ブロックを終了。
RETURN現在の処理ブロックを終了。メソッド内なら、ループの途中でもメソッドから戻る。

最初のIF内のCONTINUEをEXITに変えると、ブロック明細が見つかったところでループ全体が終わり、後ろの明細も処理されなくなります。「この1件を落とす」のか「残り全部をやめる」のかを区別しましょう。SAP公式:Loops/Program Structure

呼出先は、入る前に入力・出力・例外を見る

メソッド、汎用モジュール、FORMを見つけるたびに全部読み始めると、元の調査目的を見失います。先に、入力と出力の対応、例外、DB更新や外部送信の有無を整理します。

次はメソッド呼出し部分だけの架空例です。クラス定義は省略しています。

lo_reader->read_items(
  EXPORTING
    iv_bukrs = p_bukrs
  IMPORTING
    et_items = lt_items ).

呼出し側のEXPORTINGは値を渡す方向、IMPORTINGは受け取る方向です。宣言側のIMPORTINGと、呼出し側のEXPORTINGが対応します。このiv_bukrs = p_bukrsは、左が仮引数、右が実際に渡す値という対応付けです。

CHANGINGは入出力、RETURNINGは戻り値として確認します。接頭辞から推測するだけでなく、実際のシグネチャを見ます。SAP公式:Declaring and Calling Methods

呼出し前には正しい値があり、呼出し後に初めておかしくなるなら、その呼出先を深く読みます。クラスベース例外はRAISINGとTRY ... CATCHも追い、正常時と例外時の経路を分けます。メソッドの成功を一律にsy-subrc = 0で判断しないようにします。

sy-subrcは「どの文の結果か」を確認する

sy-subrcは、さまざまな文が設定する戻りコードです。意味は文ごとに異なり、0以外がいつも異常というわけではありません。後続の別の文で上書きされることもあります。SAP公式:ABAP System Fields

READ TABLE lt_items INTO DATA(ls_found)
  WITH KEY doc_id  = 'D003'
           item_no = '000010'.
IF sy-subrc = 0.
  " Found: inspect ls_found here
ENDIF.

この判定は、直前のREAD TABLEの結果です。調べているSELECTや関数呼出しから離れた位置のsy-subrcを見て、元の処理結果だと思い込まないようにします。必要なら、対象操作直後に別変数へ保存しているかも確認します。

また、READ TABLEで見つからなかったのに、作業領域に残っている値を今回取得したデータだと解釈しないことも大切です。結果コードと値をセットで読みます。

デバッグは「最初に期待と違う地点」を探す

静的に読んだら、仮説を一つ作ってから止めます。今回なら「D003はSELECTでは取れているが、ブロック判定で除外されるはず」という仮説です。

  • SELECT直後:lt_itemsにD003があるか。取得件数とキーで確認する。
  • 対象行の判定直前:ls_item-doc_idがD003になる条件付きブレークポイントで、blockedとamountを見る。
  • APPEND付近:D003で到達するかを確認する。到達しないなら直前の分岐を追う。
  • 出力直前:lt_resultに何が残っているか。結果が正しければ表示側へ調査を移す。

呼出元が分からないときは、コールスタックで「誰から、どの経路で呼ばれたか」を確認します。値を書き換えた箇所を探すときは、ウォッチポイントで変更時に止める方法もあります。過去の変更履歴が自動で残っている機能ではありません。

ADTではブレークポイント、変数の確認、ステップ実行、コールスタックを使えます。画面名や利用できる機能はADTとバックエンドのバージョン・権限により異なります。SAP公式:ABAP Debugging in ADT

調査でも、プログラムを動かせば更新・メール送信・外部連携が走る場合があります。承認された開発・検証環境とテストデータを優先し、本番でのデバッグ、変数変更、処理の飛び越し、ジョブ再実行は自己判断で行わないでください。

UPDATE、INSERT、DELETE、MODIFY、COMMIT WORK、更新タスク、RFC・API呼出しなどがあれば、更新・確定・外部への影響を先に把握します。ROLLBACKすればメールや外部システムまで必ず元に戻る、とは考えないようにします。

ABAP Cloud・RAPでは入口を読み替える

ABAP Cloudは、公開されたAPIと制限されたABAP言語バージョンを使う開発モデルです。Public Editionだけを指す言葉ではなく、他の対応環境でも使われます。SAP公式:Developing with ABAP Cloud/Private Editionでの開発モデル

RAPの調査では、サービス、CDSエンティティ、ビヘイビア定義・実装、実行された操作などを入口として確認します。PARAMETERS → START-OF-SELECTIONを共通の入口にせず、どのリクエストがどの処理を起動したかを押さえます。

環境が変わっても、入力 → 取得 → 判定・加工 → 呼出し → 結果を追う考え方は使えます。トランザクションやAPIへのアクセス可否は、対象環境の制約と権限を確認してください。

調査メモは、事実・仮説・次の確認を分けて残す

最後に「何となく分かった」で終わらせず、第三者が同じ箇所を確認できる形でメモを残します。今回の例なら、次の程度でも調査の引き継ぎに使えます。

  • 対象:架空レポートzread_flow_demo。会社1000、JPY、最低金額10,000。
  • コードから分かる事実:D003のblockedがXなら、最初のIF内のCONTINUEでAPPENDに進まない。
  • 確認する実行時の値:SELECT直後にD003が存在するか、判定時のblockedがXか。
  • 業務上の確認:ブロック明細を除外する仕様でよいか。ブロック値の設定元はどこか。
  • 次の一手:許可された環境で仮説を確かめ、コードの動作と期待仕様の差を報告する。

実務での読解は、全行を暗記する作業ではありません。調査対象を決め、入口を特定し、対象の1件がどこまで残るかを追う。その順番を持つと、長いABAPソースでも次に見る場所を絞りやすくなります。

なお、環境ごとのコード差が疑われる場合は、STMSで移送履歴を調べる方法と合わせて、有効なバージョンや反映状況を確認してください。

コメント

タイトルとURLをコピーしました