動的処理(フィールドシンボル)
24.1 動的処理(フィールドシンボル)
フィールドシンボルは、データオブジェクトを間接的に参照するための仕組みです。
値そのものをコピーして保持するのではなく、「どのデータを参照しているか」という参照(カーソルのようなイメージ)を扱います。
言葉で説明しても難しいため、実際の使われ方を見て理解をしていきましょう。
フィールドシンボルの宣言
フィールドシンボルは変数と同様に使用前に宣言できます。
構文
FIELD-SYMBOLS: <フィールドシンボル名> TYPE <データ型>.
フィールドシンボルシンボル名は<>(括弧)で囲む必要があります。データ型を指定しない場合、「ANY」を使用することも可能ですが、データ型が指定可能な場合は指定することが望ましいです。
S/4HANA(ABAP 7.40+)では、インライン宣言を使って次のように書くのが一般的です。
・複数行:LOOP AT … ASSIGNING FIELD-SYMBOL(<fs>).
・1件検索:READ TABLE … ASSIGNING FIELD-SYMBOL(<fs>) …
READ TABLE の場合は見つからないことがあるため、必ずsy-subrcを確認します。
フィールドシンボルを使わない場合/使う場合の違い
LOOP AT IT_TEST ASSIGNING <FS_TEST>.
CASE SY-TABIX.
WHEN 1.
<FS_TEST>-NAME = ‘アキヤマ’.
WHEN 2.
<FS_TEST>-NAME = ‘ヤマモト’.
WHEN 3.
<FS_TEST>-NAME = ‘ババ’.
ENDCASE.
ENDLOOP.
この形式は、ループ中で <FS_TEST>-… を変更すると、内部テーブルの行そのものが更新されます。 (コピーではなく参照のため) そのため、MODIFYを書かなくても、更新した内容がそのまま反映されます。
LOOP AT IT_TEST INTO ST_TEST.
CASE SY-TABIX.
WHEN 1.
ST_TEST-NAME = ‘アキヤマ’.
WHEN 2.
ST_TEST-NAME = ‘ヤマモト’.
WHEN 3.
ST_TEST-NAME = ‘ババ’.
ENDCASE.
MODIFY IT_TEST FROM ST_TEST INDEX SY-TABIX.
ENDLOOP.
「LOOP AT itab INTO wa」は、行を作業領域へコピーします。
内部テーブルへ変更を反映したい場合は、「MODIFY IT_TEST FROM ST_TEST INDEX
SY-TABIX.」のように 明示的に更新する必要があります。
内部テーブルを処理するとき、「LOOP … INTO」のように作業領域へ行をコピーして編集すると、大量データではコピー回数が増え、無駄な処理コストが発生します。
これに対してフィールドシンボルを使う「LOOP … ASSIGNING」では、行をコピーせずに参照したまま扱えるため、「参照しながら編集する」用途に向いています。
サンプルコード①(動作の違い)
■サンプルコード①(動作の違い)
*&---------------------------------------------------------------------*
*& Report Z_SAMPLE241_FIELDSYMBOL
*&---------------------------------------------------------------------*
*& 概要:フィールドシンボルの有無による「編集結果の違い」を確認する(旧構文)
*&---------------------------------------------------------------------*
REPORT Z_SAMPLE241_FIELDSYMBOL.
TYPES: BEGIN OF TY_SALE,
MATNR TYPE MATNR,
ZTEXT TYPE CHAR20,
AMT TYPE P LENGTH 11 DECIMALS 2,
WAERS TYPE WAERS,
END OF TY_SALE.
DATA: LT_SALE TYPE STANDARD TABLE OF TY_SALE WITH DEFAULT KEY,
LS_SALE TYPE TY_SALE,
LS_COPY TYPE TY_SALE,
LS_DISP TYPE TY_SALE.
" ★フィールドシンボル
FIELD-SYMBOLS <LS_SALE_FS> TYPE TY_SALE.
START-OF-SELECTION.
"------------------------------------------------------------
" データ準備(1回目:INTO用)
"------------------------------------------------------------
CLEAR LT_SALE.
CLEAR LS_SALE.
LS_SALE-MATNR = 'MA001'.
LS_SALE-ZTEXT = 'テレビ'.
LS_SALE-AMT = 1000.
LS_SALE-WAERS = 'JPY'.
APPEND LS_SALE TO LT_SALE.
CLEAR LS_SALE.
LS_SALE-MATNR = 'MA002'.
LS_SALE-ZTEXT = '冷蔵庫'.
LS_SALE-AMT = 200000.
LS_SALE-WAERS = 'JPY'.
APPEND LS_SALE TO LT_SALE.
CLEAR LS_SALE.
LS_SALE-MATNR = 'MA003'.
LS_SALE-ZTEXT = 'エアコン'.
LS_SALE-AMT = 3000.
LS_SALE-WAERS = 'JPY'.
APPEND LS_SALE TO LT_SALE.
WRITE: / '① INTO(コピー)で編集 → 内部テーブルは変わらない'.
LOOP AT LT_SALE INTO LS_COPY.
CASE SY-TABIX.
WHEN 1. LS_COPY-ZTEXT = 'テスト1'.
WHEN 2. LS_COPY-ZTEXT = 'テスト2'.
WHEN 3. LS_COPY-ZTEXT = 'テスト3'.
ENDCASE.
" ※MODIFYしていないので lt_sale は変わらない
ENDLOOP.
ULINE.
WRITE: / '結果(lt_sale の中身)'.
LOOP AT LT_SALE INTO LS_DISP.
WRITE: / LS_DISP-MATNR, LS_DISP-ZTEXT, LS_DISP-AMT, LS_DISP-WAERS.
ENDLOOP.
SKIP 2.
"------------------------------------------------------------
" データ準備(2回目:ASSIGNING用に初期状態へ戻す)
"------------------------------------------------------------
CLEAR LT_SALE, LS_SALE.
LS_SALE-MATNR = 'MA001'.
LS_SALE-ZTEXT = 'テレビ'.
LS_SALE-AMT = 1000.
LS_SALE-WAERS = 'JPY'.
APPEND LS_SALE TO LT_SALE.
CLEAR LS_SALE.
LS_SALE-MATNR = 'MA002'.
LS_SALE-ZTEXT = '冷蔵庫'.
LS_SALE-AMT = 200000.
LS_SALE-WAERS = 'JPY'.
APPEND LS_SALE TO LT_SALE.
CLEAR LS_SALE.
LS_SALE-MATNR = 'MA003'.
LS_SALE-ZTEXT = 'エアコン'.
LS_SALE-AMT = 3000.
LS_SALE-WAERS = 'JPY'.
APPEND LS_SALE TO LT_SALE.
WRITE: / '② ASSIGNING(参照)で編集 → 内部テーブルが直接変わる'.
LOOP AT LT_SALE ASSIGNING <LS_SALE_FS>.
CASE SY-TABIX.
WHEN 1. <LS_SALE_FS>-ZTEXT = 'テスト1'.
WHEN 2. <LS_SALE_FS>-ZTEXT = 'テスト2'.
WHEN 3. <LS_SALE_FS>-ZTEXT = 'テスト3'.
ENDCASE.
ENDLOOP.
" 1件検索(READ ... ASSIGNING)の例(同じFSを再利用)
READ TABLE LT_SALE ASSIGNING <LS_SALE_FS>
WITH KEY MATNR = 'MA002'.
IF SY-SUBRC = 0.
<LS_SALE_FS>-AMT = <LS_SALE_FS>-AMT + 1.
ENDIF.
ULINE.
WRITE: / '結果(lt_sale の中身)'.
LOOP AT LT_SALE INTO LS_DISP.
WRITE: / LS_DISP-MATNR, LS_DISP-ZTEXT, LS_DISP-AMT, LS_DISP-WAERS.
ENDLOOP.
フィールドシンボルを利用しない場合は、LOOP AT … INTO のように、内部テーブルの1行を構造(作業領域)へコピーして処理します。
一方、フィールドシンボルを利用する場合は LOOP AT … ASSIGNING <フィールドシンボル> のように、行を構造へコピーせず、行を参照したまま処理します。
フィールドシンボル名は必ず <> で囲み、通常の変数と区別します。
フィールドシンボルはデータそのものを保持するのではなく、参照先(データの位置)を指し示す仕組みです。
そのため、<フィールドシンボル>-項目 = … のように代入すると、
参照先の項目に対して代入が行われ、内部テーブルの該当行が直接更新されます。
LOOP … INTO は行をコピーして処理するため、作業領域を変更しても内部テーブルは変わりません。
内部テーブルに反映したい場合は、MODIFY による書き戻しが必要です。
一方、LOOP … ASSIGNING は行を参照して処理するため、フィールドシンボル経由の変更がそのまま内部テーブルに反映されます。
同じ行を更新する処理では MODIFY を省略でき、記述もシンプルになります。
また、ASSIGNING は行のコピーを行わないため、ループ中の余分な代入や書き戻しを減らせます。
ただし、処理の効果はデータ量や内容に依存するため、
本テキストでは「コピーを減らせる」「更新を自然に書ける」という点を主に理解しておきましょう。
フィールドシンボルは便利ですが、参照先が割り当てられていない状態(未割当)で参照すると実行時エラーになり得ます。
そのため、ループ処理のように存在が確実な場面では ASSIGNING を使い、
存在が不明な1件取得では READ TABLE … ASSIGNING を使用して sy-subrc を必ず確認します。
未割当の可能性が高い場合や安全性を優先したい場合は INTO(コピー)を使い、
必要に応じて MODIFY で反映する、という使い分けを覚えて実務で活用しましょう。
フィールドシンボルの宣言(新構文)
ABAP 7.40以降では、フィールドシンボルは インライン宣言で書くのが一般的です。
「LOOP … ASSIGNING」や「READ TABLE … ASSIGNING」の中で、次のように宣言と割り当てを同時に行えます。
構文①
READ TABLE (内部テーブル) WITH KEY (項目名) = (値) ASSIGNING FIELD-SYMBOL(<FS_TEST>). IF SY-SUBRC = 0. [処理] ENDIF.
構文②
LOOP AT (内部テーブル) ASSIGNING FIELD-SYMBOL(<FS_TEST>). [処理] ENDLOOP.
1件だけの場合はREAD、複数ある場合はLOOPと覚えましょう。
■サンプルコード②(新構文)
*&---------------------------------------------------------------------*
*& Report Z_SAMPLE241_FIELDSYMBOL
*&---------------------------------------------------------------------*
*& 概要:INTO(コピー)と ASSIGNING(参照)の違いを確認する(新構文)
*&---------------------------------------------------------------------*
REPORT Z_SAMPLE241_FIELDSYMBOL_NEW.
TYPES: BEGIN OF TY_SALE,
MATNR TYPE MATNR,
ZTEXT TYPE CHAR20,
AMT TYPE P LENGTH 11 DECIMALS 2,
WAERS TYPE WAERS,
END OF TY_SALE.
TYPES TT_SALE TYPE STANDARD TABLE OF TY_SALE WITH EMPTY KEY.
" ★フィールドシンボルは1つだけ(事前宣言しない場合は LOOP/READ でインライン宣言でもOK)
FIELD-SYMBOLS <LS_SALE_FS> TYPE TY_SALE.
START-OF-SELECTION.
DATA(LT_SALE) = VALUE TT_SALE(
( MATNR = 'MA001' ZTEXT = 'テレビ' AMT = 1000 WAERS = 'JPY' )
( MATNR = 'MA002' ZTEXT = '冷蔵庫' AMT = 200000 WAERS = 'JPY' )
( MATNR = 'MA003' ZTEXT = 'エアコン' AMT = 3000 WAERS = 'JPY' )
).
WRITE: / '① INTO(コピー)で編集 → 内部テーブルは変わらない'.
LOOP AT LT_SALE INTO DATA(LS_COPY).
CASE SY-TABIX.
WHEN 1. LS_COPY-ZTEXT = 'テスト1'.
WHEN 2. LS_COPY-ZTEXT = 'テスト2'.
WHEN 3. LS_COPY-ZTEXT = 'テスト3'.
ENDCASE.
ENDLOOP.
ULINE.
WRITE: / '結果(lt_sale の中身)'.
LOOP AT LT_SALE INTO DATA(LS_DISP1).
WRITE: / LS_DISP1-MATNR, LS_DISP1-ZTEXT, LS_DISP1-AMT, LS_DISP1-WAERS.
ENDLOOP.
SKIP 2.
LT_SALE = VALUE TT_SALE(
( MATNR = 'MA001' ZTEXT = 'テレビ' AMT = 1000 WAERS = 'JPY' )
( MATNR = 'MA002' ZTEXT = '冷蔵庫' AMT = 200000 WAERS = 'JPY' )
( MATNR = 'MA003' ZTEXT = 'エアコン' AMT = 3000 WAERS = 'JPY' )
).
WRITE: / '② ASSIGNING(参照)で編集 → 内部テーブルが直接変わる'.
LOOP AT LT_SALE ASSIGNING <LS_SALE_FS>.
CASE SY-TABIX.
WHEN 1. <LS_SALE_FS>-ZTEXT = 'テスト1'.
WHEN 2. <LS_SALE_FS>-ZTEXT = 'テスト2'.
WHEN 3. <LS_SALE_FS>-ZTEXT = 'テスト3'.
ENDCASE.
ENDLOOP.
" 1件検索(READ ... ASSIGNING)の例(同じFSを再利用)
READ TABLE LT_SALE ASSIGNING <LS_SALE_FS>
WITH KEY MATNR = 'MA002'.
IF SY-SUBRC = 0.
<LS_SALE_FS>-AMT = <LS_SALE_FS>-AMT + 1.
ENDIF.
ULINE.
WRITE: / '結果(lt_sale の中身)'.
LOOP AT LT_SALE INTO DATA(LS_DISP2).
WRITE: / LS_DISP2-MATNR, LS_DISP2-ZTEXT, LS_DISP2-AMT, LS_DISP2-WAERS.
ENDLOOP. " FRM_FS_IT