動的処理(フィールドシンボル)

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

NEXT>> 24.2 メモリ領域の使用

研修の詳細はこちら