内部テーブルの照会(READ、SORT、DESCRIBE)

15.5 内部テーブルの照会(READ、SORT、DESCRIBE)

ここでは、内部テーブルの照会方法として以下を説明します。
内部テーブルを操作する方法として、下記の3つがあります。

・READ
・SORT
・DESCRIBE


READ

READ TABLE命令による内部テーブルの読み込み処理について解説します。
READ TABLE命令は、内部テーブルの単一行を抽出する命令です。
主にREAD TABLE命令の場合は、内部テーブルのレコードを抽出する場合に用います。

READ TABLE命令は指定した内部テーブルから単一行を読み込み、指定した構造に格納します。
読み込むレコードは、WITH KEYオプションで指定します。

例えば「WITH KEY VBELN = ‘100000000’」 とすれば、受注伝票 100000000のレコード(行)が対象となります。
もし、読み込む行が一意にならない場合は、最初に一致するレコードが読み込み対象となります。
したがって、キーの指定が不十分な場合(レコードを一意に特定できない場合)には、意図したものと 違うレコードが抽出されてしまいます。
基本は、レコードを一意に指定できるようにKEYを指定しましょう。

あまり使用されませんがINDEXオプションにて、行番号を指定することも可能です。

READ TABLE命令のイメージ

「サンプルコード」①
REPORT Z_SAMPLE155.

*型定義
TYPES:
  BEGIN OF TY_RECORD,
    COLP TYPE I,
    COLQ TYPE I,
  END OF TY_RECORD.

*変数定義
DATA:
  LT_MYTABLE TYPE STANDARD TABLE OF TYP_RECORD,
  LS_RECORD TYPE TYP_RECORD.

*主処理
START-OF-SELECTION.

  "内部テーブル作成(6行)
  DO 6 TIMES.
    LS_RECORD-COLP = SY-INDEX.
    LS_RECORD-COLQ = SY-INDEX + 5.
    APPEND LS_RECORD TO LT_MYTABLE.
  ENDDO.

*READ命令(単一行を抽出)
  READ TABLE LT_MYTABLE INTO LS_RECORD WITH KEY COLQ = 8.

*抽出できなかった場合、処理終了
  IF SY-SUBRC <> 0.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDIF.

*出力
  WRITE: / LS_RECORD-COLP, LS_RECORD-COLQ.

END-OF-SELECTION.

このサンプルでは、DO ループで6行の内部テーブルを作り、COLQ = 8 の行を READ TABLEで1行取り出します。
READの成否は SY-SUBRCで判定し、見つからない場合はエラーメッセージで終了します。
また、DOの繰り返し回数を示す SY-INDEX を使って値を作っています。

実行結果

補足(SY-INDEXについて)

先述の通り、SY-INDEXは、現在のループパス件数が格納されるシステム変数です。
詳しくは20章で学習しますので、今回は簡単に解説します。

SY-INDEXは データ型が 「INT4」なので数値が格納されます。
「DO 6 TIMES」で6回繰り返しなさい、という命令なので、 COLPには1、2、3、4、5、6が格納されます。
COLQには5加算された、6、7、8、9、10、11が格納されます。

補足(SY-SUBRCについて)

SY-SUBRCもシステム変数の1つです。
詳しくは20章で学ぶため、 当章ではABAP命令が失敗すると「0以外の数字」が格納される変数、と理解しましょう。

IF SY-SUBRC <> 0.
<処理>
ENDIF.
上記のように記述すると、直前の処理が失敗したら<処理> を実行する、という意味になります。

また、MESSAGE E019(ZTEST2021)は、 エラーメッセージを出力して処理を終了する、という命令です。
詳しくは18章で学習するため、プログラムの流れが理解できればOKです。

デバッグ機能を利用し、READ TABLE命令実行時の値を確認

READ(新構文)

S/4 HANA化に伴い、READ命令の記述方法が変更され、より便利に扱えるようになりました。

従来までの記述方法と大きく変わりますが、動作としては基本的に同じです。
ただし、旧構文では内部テーブルの単一行を抽出していましたが 新構文では単一行の抽出に加えて、抽出した行の項目(列)も指定することができるようになりました。

実務ではどちらも用いられるため、旧構文と新構文の違いを理解して 柔軟に対応できるようにしましょう。

構文は以下の通りです。
項目は、「-(ハイフン)」で指定します。

「サンプルコード」②
REPORT Z_SAMPLE155_MODIFY.

*型定義
TYPES:
  BEGIN OF TY_RECORD,
    COLP TYPE I,
    COLQ TYPE I,
  END OF TY_RECORD.

*変数定義
DATA:
  LT_MYTABLE TYPE STANDARD TABLE OF TYP_RECORD,
  LS_RECORD TYPE TY_RECORD.

*主処理
START-OF-SELECTION.

*内部テーブル作成
  DO 6 TIMES.
    LS_RECORD-COLP = SY-INDEX.
    LS_RECORD-COLQ = SY-INDEX + 5.
    APPEND LS_RECORD TO LT_MYTABLE.
  ENDDO.

*READ命令(単一行を抽出)
  TRY.
    LS_RECORD = LT_MYTABLE[ COLQ = 8 ].
*抽出できなかった場合、処理終了
  CATCH CX_SY_ITAB_LINE_NOT_FOUND.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDTRY.

*出力
  WRITE: / LS_RECORD-COLP,LS_RECORD-COLQ.

END-OF-SELECTION.

実行結果

実行結果は同じになります。

補足(TRY~ENDTRYについて)

新構文にてデータが読み込めない場合、ショートダンプが発生します。ショートダンプを防ぐためのコードが以下です。

 TRY.
    < READ(新構文) >
 CATCH CX_SY_ITAB_LINE_NOT_FOUND.
    < 存在しない場合の処理 >
 ENDTRY.

例外処理と呼ばれますが、今はREAD命令時にショートダンプを防ぐものだと理解できればOKです。
また、ショートダンプを防ぐための記述方法がいくつかあるため、紹介します。
詳しく理解しようとせず、色々な記述方法があると認識できれば良いでしょう。

「サンプルコード」③
REPORT Z_SAMPLE155_LINEEXISTS.

~割愛~

*READ命令(単一行を抽出)
  IF LINE_EXISTS( IT_MYTABLE[ COLQ = 8 ] ).
    ST_RECORD = IT_MYTABLE[ COLQ = 8 ].
*抽出できなかった場合、処理終了
  ELSE.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDIF.

~割愛~

補足(LINE_EXISTSについて)

LINE_EXISTSは条件式(IF文)で用いられます。 詳しくは19章で学習するため、括弧内のデータが存在するかチェックする命令という点を確認しておきます。

IF LINE_EXISTS( < READ(新構文) > ).  ← 存在チェック(LINE_EXISTS命令)
	< READ(新構文) >               ← 単一行/項目を取得(READ命令)
ELSE. 
	<存在しない場合の処理>            ← エラー処理(存在しない場合の処理)
ENDIF.

事前に抽出データが存在するかを確認して、存在していればREAD(抽出)し、在しなければ <存在しない場合の処理>を実行するというコードになります。

データが存在する場合のみREADするため、ショートダンプを防ぎます。

「サンプルコード」④
REPORT Z_SAMPLE155_OPTIONAL.

~割愛~

*READ命令(単一行を抽出)
  LS_RECORD = VALUE #( LT_MYTABLE[ COLQ = 8 ] OPTIONAL ).

*抽出できなかった場合、処理終了
  IF LS_RECORD IS INITIAL.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDIF.

~割愛~

補足(OPTIONALについて)

OPTIONALはVALUE命令と組み合わせて使用します。
OPTIONALを指定した場合は、データが読み込めない場合でもショートダンプが発生しません。
また、<構造 | 変数>には初期値が設定されます。

補足(INITIALについて)

INITIALは条件式(IF文)で用いられます。 詳しくは19章で学習するため、初期値かどうかチェックする命令だと理解できればOKです。

IF ST_RECORD IS INITIAL.
 < 処理 >
ENDIF.

ST_RECORDが初期値の場合、<処理>を実行するというコードになります。

「サンプルコード」➄
REPORT Z_SAMPLE155_NEW.

*型定義
TYPES:
  BEGIN OF TY_RECORD,
    COLP TYPE I,
    COLQ TYPE I,
  END OF TY_RECORD.

*変数定義
DATA:
  LT_MYTABLE TYPE STANDARD TABLE OF TY_RECORD,
  LS_RECORD TYPE TY_RECORD.

*主処理
START-OF-SELECTION.

*内部テーブル作成
  DO 6 TIMES.
    LS_RECORD-COLP = SY-INDEX.
    LS_RECORD-COLQ = SY-INDEX + 5.
    APPEND LS_RECORD TO LT_MYTABLE.
  ENDDO.

*READ命令(単一行を抽出)
  TRY.
    DATA(WK_COLQ) = LT_MYTABLE[ COLQ = 8 ]-COLQ	.
*抽出できなかった場合、処理終了
  CATCH CX_SY_ITAB_LINE_NOT_FOUND.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDTRY.

*出力
  WRITE: / ‘WK_COLQ =’,WK_COLQ.

END-OF-SELECTION.

実行結果

項目を指定することも可能です。

SORT

内部テーブルを昇順・降順に並び替えるSORT命令について解説します。
SORT命令は並び替え(ソートの優先順や、昇順・降順の混合など)を行う命令です。
SORT命令の処理は、キーによって内部テーブルのレコードをソートします。

この命令では、明示的なソートキーを指定せず、内部テーブルをソートします。
明示的なソートキーを 指定しないと、内部テーブルはテーブルキーに従ってソートされます。
オプションASCENDING(昇順)または DESCENDING(降順)を使用して、ソート順を明示的に指定することができます。

指定しない場合、デフォルトで昇順にソートされます。
・ASCENDING  ⇒ 昇順(省略時デフォルト)
・DESCENDING ⇒ 降順

昇順ソートの例

内部テーブル:LT_TABのテーブルキーがNUMBERの場合、
SORT LT_TAB.
と記述とすれば、図のようにレコードが並び変えられます。

SORT命令は、キー項目であるNUMBER項目を優先してソートします。
キーは、内部テーブルの宣言時に指定しているものや、標準テーブルのキー項目を参照している項目が 該当します。

また、ASCENDING / DESCENDINGオプションを指定していないため、デフォルトで昇順に 並び替えされています。

つまり、「SORT」だけ記載した場合でも、昇順ソートを明示的に指定した、
SORT LT_TAB ASCENDING.
と同じ意味になります。

ASCENDINGオプションはデフォルトなので、指定する必要性はありませんが、ソースコードの可読性向上を目的として明示する場合があります。

降順ソートの例

内部テーブル:ITAB1のキー項目がNUMBERの場合、
SORT LT_TAB DESCENDING.
と記述した場合、NUMBERをキーに降順に並び替えされます。

SORT BY

どの項目で、昇順/降順のどちらで並び替えるかを明示できます。 明示的なソートキーを指定し、内部テーブルを昇順・降順に並び替えるSORT BY命令について解説します。

内部テーブルを、明示的に指定した項目をソートキーとして並び替えます。
ソートキーは複数指定することができますが、複数項目を指定した場合は、左から順に優先順位が高いキーとして扱われます。

オプションASCENDING(昇順)または DESCENDING(降順)を使用することで、ソート順を明示的に指定することができます。

指定しない場合、デフォルトで昇順にソートされます。
・ASCENDING  ⇒ 昇順(デフォルト)
・DESCENDING ⇒ 降順

「サンプルコード」①
REPORT Z_SAMPLE156.

*主処理
START-OF-SELECTION.

*データ抽出
  SELECT FROM ZTEST_TBL_001
  FIELDS CLASS, “組
         NUM,            “出席番号
         LASTNAME, “姓
         FIRSTNAME “名
    INTO TABLE @DATA(LT_STUDENTS).

*抽出できなかった場合、処理終了
  IF SY-SUBRC <> 0.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDIF.

*ソート
  SORT LT_STUDENTS BY NUM  ASCENDING. "出席番号(昇順)

*抽出結果を出力
  WRITE: /  '組',
         15 '出席番号',
         30 '姓',
         45 '名'.
  LOOP AT LT_STUDENTS INTO DATA(LS_STUDENTS).
     WRITE: /  LS_STUDENTS-CLASS,     "組
            15 LS_STUDENTS-NUM,             "出席番号
            30 LS_STUDENTS-LASTNAME,  "姓
            45 LS_STUDENTS-FIRSTNAME. "名
  ENDLOOP.

END-OF-SELECTION.
	
実行結果

■デバッグ機能を利用し、SORT BY ASCENDING命令実行時の値を確認

項目:NUM(出席番号)の昇順でソートされていることが確認できます。

「サンプルコード」②
REPORT Z_SAMPLE156_MODIFY.

*主処理
START-OF-SELECTION.

*データ抽出
  SELECT FROM ZTEST_TBL_001
  FIELDS CLASS,    "組
         NUM,      "出席番号
         LASTNAME, "姓
         FIRSTNAME "名
    INTO TABLE @DATA(LT_STUDENTS).

*抽出できなかった場合、処理終了
  IF SY-SUBRC <> 0.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDIF.

*ソート
  SORT LT_STUDENTS BY CLASS DESCENDING "組           (降順)
                                       NUM DESCENDING."出席番号(降順)

*抽出結果を出力
  WRITE: /  '組',
         15 '出席番号',
         30 '姓',
         45 '名'.
  LOOP AT LT_STUDENTS INTO DATA(LS_STUDENTS).
     WRITE: /  LS_STUDENTS-CLASS,     "組
            15 LS_STUDENTS-NUM,       "出席番号
            30 LS_STUDENTS-LASTNAME,  "姓
            45 LS_STUDENTS-FIRSTNAME. "名
  ENDLOOP.

END-OF-SELECTION.
実行結果

■デバッグ機能を利用し、SORT BY DESCENDING 命令実行時の値を確認

項目:CLASS(組)の降順(優先順位1位)
項目:NUM(出席番号)の降順(優先順位2位)
でソートされていることが確認できます。

前述の通り、2項目以上ソートする場合は、指定した項目の順でソートされます。 また、昇順と降順を組み合わせてソートすることも可能です。

DESCRIBE

ABAPにおける内部テーブルの属性を読み込む命令「DESCRIBE TABLE」について解説します。
DESCRIBE TABLE命令は、指定した内部テーブルの属性を読み取る命令です。

読み取りたい内部テーブルの属性に応じてオプションがあり、オプションなしではDESCRIBE TABLE命令を利用することはできません。
必ず、いずれかのオプションを指定する必要があります。

DESCRIBE TABLE命令には2つのオプションがあります。
・LINES:内部テーブルの行数を取得
・KIND:内部テーブルのテーブルタイプ(標準/ソート/ハッシュ)を取得

DESCRIBE LINES

ABAPにおける内部テーブルの属性を読み込む命令「DESCRIBE TABLE」で、 LINESオプションを利用すると、内部テーブルの行数を判別することができます。
判別した行数については、LINESオプションの後に指定した変数に格納されます。

LINESオプションの後に指定する変数には、整数しか入りません。
データ型は「I(整数)」でなければなりません。

DESCRIBE LINESの例

DESCRIBE TABLE LT_TAB LINES TAB_CNT.
と記述すると、
内部テーブル「LT_TAB」の内容が以下の場合、変数「TAB_CNT」には「9」が格納されます。

DESCRIBE KIND

KINDオプションは、指定した内部テーブルのテーブルタイプを判別することができます。
KINDオプションの後に指定した変数には、テーブルタイプに応じて 以下のいずれかの文字が格納されます。

・標準テーブルの場合「T」
・ソートテーブルの場合「S」
・ハッシュテーブルの場合「H」  

「サンプルコード」①
REPORT Z_SAMPLE157.

*主処理
START-OF-SELECTION.

*データ抽出
  SELECT FROM ZTEST_TBL_001
  FIELDS *
    INTO TABLE @DATA(IT_STUDENTS).

*抽出できなかった場合、処理終了
  IF SY-SUBRC <> 0.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDIF.

*内部テーブルの行数を取得
  DESCRIBE TABLE IT_STUDENTS LINES DATA(WK_LINES).

*内部テーブルのテーブルタイプを取得
  DESCRIBE TABLE IT_STUDENTS KIND DATA(WK_KIND).

*出力
  WRITE: '<内部テーブル:IT_STUDENTS>',
         / '行数',WK_LINES,
         / 'テーブルタイプ',WK_KIND.

デバッグ機能を利用し、 DESCRIBE TABLE 命令実行時の値を確認\

内部テーブル:IT_STUDENTSは6行あります。
よって、DESCRIBE TABLE LINES命令により、内部テーブルの行数を取得すると、 変数:WK_LINESには6が入ります。

また、内部テーブル:IT_STUDENTSのテーブルはStandard Table(標準テーブル)なので、 DESCRIBE TABLE KIND命令により、内部テーブルのテーブルタイプを取得すると、 変数:WK_KINDにはTが入ります。

DESCRIBE(新構文)

DESCRIBE TABLE命令のLINESオプションには新構文があります。
行数(LINES)は、ABAP 7.4以降では関数「LINES( )」を使って簡潔に取得できます。

「サンプルコード」②
REPORT Z_SAMPLE157_NEW.

*主処理
START-OF-SELECTION.

*データ抽出
  SELECT FROM ZTEST_TBL_001
  FIELDS *
    INTO TABLE @DATA(IT_STUDENTS).

*抽出できなかった場合、処理終了
  IF SY-SUBRC <> 0.
    MESSAGE E019(ZTEST2021). "対象データが存在しません。
  ENDIF.

*内部テーブルの行数を取得(新構文)
  DATA(WK_LINES) = LINES( IT_STUDENTS ).

*出力
  WRITE: '<内部テーブル:IT_STUDENTS>',
         / '行数',WK_LINES.

END-OF-SELECTION.

実行結果

実行結果は同じになります。


NEXT>> 第16章 ABAP SQL基本

研修の詳細はこちら