SELECT(For All Entries, JOIN)

17.1 SELECT(For All Entries, JOIN)

ここでは、データベースのアクセス方法として以下を説明します。
・JOIN:DBテーブル同士を「キーで結合」して、必要な項目をまとめて取得する方法
・FOR ALL ENTRIES:検索用内部テーブルのキー群に一致するデータをまとめて取得する方法

SELECT(JOIN)

JOINは「結合」とも呼ばれ、一度のSELECT命令で、複数テーブルからレコードを抽出することが 可能です。
また、結合には内部結合と外部結合の2種類があります。

・内部結合
内部結合は、結合しているすべてのテーブルに存在するデータを抽出します。
抽出する際に、結合しているすべてのテーブルにデータが存在しない場合は、 抽出せずにスキップします。

・外部結合
外部結合は、基準となるテーブルにデータが存在する場合に抽出します。
内部結合と違い、結合しているすべてのテーブルにデータが存在しない場合も抽出します。
つまり、該当レコードが、スキップされずにすべて取得されます。

■ FROM句
結合なしのSELECT命令と同様に、FROM句には抽出先のデータベーステーブルを指定します。

■別名(AS)
AS は、SELECT文の中でテーブルに「別名(エイリアス)」を付ける書き方です。
JOINでは複数テーブルに同名の項目(例:MANDT など)が存在することが多いため、a~MANDT のように「どのテーブルの項目か」を明確にする目的で別名を付けます。
なお、別名は JOINのときだけでなく、単一テーブルのSELECTでも付けられます。
(将来JOINに発展する可能性がある場合や、可読性をそろえたい場合に使います)
この別名は、そのSELECT文の中でのみ有効で、SELECT文の外では使えない点に注意しましょう。

抽出元のデータベーステーブルは<DBテーブル1>で、<別名1>と記しますという意味です。
FROM sflight AS A のように書くと、以降そのSELECT文の中で A~carrid のように参照できます。

■ INNER JOIN(内部結合) /OUTER JOIN(外部結合)句
FROM句で指定したテーブルと結合させたいデータベーステーブルを指定することができます。
FROM句と同様に、データベーステーブルに任意の別名をつけることができます。


内部結合するデータベーステーブルは<DBテーブル2>で、<別名2>と記します、という意味です。


外部結合するデータベーステーブルは<DBテーブル2>で、<別名2>と記します、という意味です。


■ ON句
結合条件を指定します。ON句で指定した結合条件を満たしたレコードのみ抽出することができます。
条件の指定方法は、WHERE句とほぼ同様ですが、それぞれの項目名がどのデータベースの項目なのかを記述する必要があります。

データベーステーブル<DBテーブル1>の<項目>と、
データベーステーブル<DBテーブル2>の<項目>が等しいレコードを結合しなさい、という意味です。


■ WHERE句
WHERE句は抽出条件を指定します。ON句と同様に、条件を記述する際にそれぞれの項目名がどのデータベースの項目なのかを記述する必要があります。

■ SELECT INNER JOINの構文例:
SFLIGHT(フライト実績)とSPFLI(スケジュール)をCARRID/CONNIDで結合して一覧化する。

*内部結合
SELECT
 A~CARRID	“航空会社コード
 A~CONNID	“フライト接続番号
 A~FLDATE	“フライト日付
 B~CITYFROM	“到着都市
 B~CITYTO	“出発都市
FROM SFLIGHT AS A
INNER JOIN SPFLI AS B
ON A~CARRID = B~CARRID
AND A~CONNID = B~CONNID
INTO TABLE TAB_FLIGHT
WHERE A~FLDATE > ‘20251101’.

この構文例の意味について、コードの記載順に説明していきます。


SELECT
 A~CARRID	“航空会社コード
 A~CONNID	“フライト接続番号
 A~FLDATE	“フライト日付
 B~CITYFROM	“到着都市
 B~CITYTO	“出発都市

ここでは、データベーステーブルから抽出する項目を指定しています。


抽出する項目は、
Aの項目:CARRID(航空会社コード)
Aの項目:CONNID(フライト接続番号)
Aの項目:FLDATE(フライト日付)
Bの項目:CITYFROM(到着都市)
Bの項目:CITYTO(出発都市)
となります。


FROM SFLIGHT AS A

FROM句には、抽出したいデータベーステーブル名を記述します。
データベーステーブル:SFLIGHT(フライト)から抽出し、別名をAと記します。


INNER JOIN SPFLI AS B

INNER JOIN句には、 結合させたいデータベーステーブル名を記述します。
データベーステーブル:SPFLI(フライトスケジュール)を内部結合し、別名をBと記します。
INNER JOINは「両方のテーブルに存在するキーだけ」を抽出します。
そのため、教材の例のように、片方(別名B)に存在しないキーは抽出対象外になります。


ON A~CARRID = B~CARRID
AND A~CONNID = B~CONNID

ON句には、結合条件が書かれています。
Aの項目:CARRID(航空会社コード)が、Bの項目:CARRID(航空会社コード)と等しい、 かつ、Aの項目:CONNID(フライト接続番号)が、Bの項目:CONNID(フライト接続番号)に 等しいデータを結合します。
内部結合ですので、AとB両方のデータベーステーブルにデータが存在するレコードのみ結合されます。


INTO TABLE TAB_FLIGHT

INTO TABLE句には、取得したデータを格納する内部テーブルを指定します。
取得したデータは内部テーブル:TAB_FLIGHTに格納します。


WHERE A~FLDATE > ‘20251101’.

WHERE句では、抽出条件を指定しています。
Aの項目:FLDATE(フライト日付)が、2025年11月1日より後のデータが抽出対象になります。

■ 構文例の内部結合のイメージ

CARRID(航空会社コード)がLH、CONNID(フライト接続番号)が0400のレコードは、
別名:Bにレコードが存在しないため、抽出対象外となります。


■ SELECT LEFT OUTER JOINの構文例:

INNER JOIN と違い、「左側(A)」を残しつつ、結合先が無い場合は結合先項目を初期値にする。

*外部結合
SELECT
 A~CARRID	“航空会社コード
 A~CONNID	“フライト接続番号
 A~FLDATE	“フライト日付
 B~CITYFROM	“到着都市
 B~CITYTO	“出発都市
FROM SFLIGHT AS A
LEFT OUTER JOIN SPFLI AS B
ON A~CARRID = B~CARRID
AND A~CONNID = B~CONNID
INTO TABLE TAB_FLIGHT
WHERE A~FLDATE > ‘20251101’.

先ほどの内部結合の構文例との違いは、青字部分の記載が、
LEFT OUTER JOIN(外部結合)か、INNER JOIN(内部結合)か、のみです。

この構文例の意味については、コードの違う以下の部分についてのみ説明します。

LEFT OUTER JOIN SPFLI AS B

LEFT OUTER JOIN句には、 結合させたいデータベーステーブル名を記述します。
データベーステーブル:SPFLI(フライトスケジュール)を外部結合し、別名をBと記します。


LEFT OUTER JOINは「左側(FROM側)の行を残す」結合です。
結合先(別名B)にレコードが無い場合、「B側の項目は初期値」になって出力されます。
INNER JOIN と「抽出対象が変わる」点に注目します。

■ 構文例の外部結合のイメージ

CARRID(航空会社コード)がLH、CONNID(フライト接続番号)が0400のデータは、 別名:Aには存在し、別名:Bに存在しませんが、外部結合では抽出対象となります。
別名:Bから取得できなかった項目:CITYFROM(出発都市)と、CITYTO(到着都市)は、 内部テーブル:TAB_FLIGHTでは、ブランクになります。

SELECT(FOR ALL ENTRIES)

FOR ALL ENTRIES命令では、指定した内部テーブルに含まれるレコードの項目の組み合わせを抽出対象として、一括検索することができます。

内部テーブルの値を1件ずつ抽出条件に指定しなくても、FOR ALL ENTRIESを使えば、 一括で抽出することができます。

<注意事項>
指定した検索用内部テーブルが0件の場合は、全件選択となってしまうため、 FOR ALL ENTRIES を使用する前には、必ずIF命令などを利用して、検索用内部テーブルの件数を確認しましょう。


■ データが読み込まれるまでの流れ


この例では、検索用内部テーブルを検索条件として、DBテーブルからデータを読み込み 取得用内部テーブルに格納しています。

すなわち、DBテーブルから
項目:Aの値が001、かつ、項目:Bの値がAのレコード、
および
項目:Aの値が003、かつ、項目:Bの値がCのレコード、
を読み込み、取得用内部テーブルに格納しています。


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

START-OF-SELECTION.

*(1) 生徒情報マスタ(ZTEST_TBL_001)から以下のデータを内部テーブルへ読み込みます。
  SELECT *
     FROM ZTEST_TBL_001 AS M
     WHERE M~GRADE = 1
       AND M~CLASS = 'A'
       AND M~NUM   <= 2
     INTO TABLE @DATA(LT_INDEX).

  "※重要:0件だと FOR ALL ENTRIES は全件抽出になるため、必ずガードする
  IF LT_INDEX IS INITIAL.
    WRITE: / '検索用内部テーブルが0件のため、抽出を行いません。'.
    RETURN.
  ENDIF.

*(2) (1)で用意した内部テーブルを検索用内部テーブルとして、
*   生徒別テスト得点情報テーブル(ZTEST_TBL_002)の読み込みを行います。
*   以下の生徒別テスト得点情報テーブルデータから、
*   検索用内部テーブルと検索キー項目(grade,class,num)が一致するデータを読み込みます。
  SELECT *
    FROM ZTEST_TBL_002 AS D
    INTO TABLE @DATA(LT_DATA)
    FOR ALL ENTRIES IN @LT_INDEX
    WHERE D~GRADE = @LT_INDEX-GRADE
      AND D~CLASS = @LT_INDEX-CLASS
      AND D~NUM   = @LT_INDEX-NUM.

*ヘッダの帳票出力
  WRITE: 001(4)  '学年',
         010(2)  '組',
         020(8)  '出席番号',
         030(10) 'テスト名',
         050(4)  '国語',
         055(4)  '英語',
         060(4)  '数学'.
        ULINE.

  LOOP AT LT_DATA INTO DATA(LS_DATA).
    WRITE: /001(1)  LS_DATA-GRADE,
            010(2)  LS_DATA-CLASS,
            020(2)  LS_DATA-NUM,
            030(10) LS_DATA-TESTNAME,
            050(3)  LS_DATA-JAPANESE,
            055(3)  LS_DATA-ENGLISH,
            060(3)  LS_DATA-MATH.
    NEW-LINE.
  ENDLOOP. 

■ データが読み込まれるまでの流れ(イメージ)


■ データが読み込まれるまでの流れ(解説)
データベーステーブル:ZTEST_TBL_002(生徒別テスト得点情報テーブル)から、
項目:GRADE(学年)、CLASS(組)、NUM(出席番号)の値が、
検索用内部テーブル:T_INDEXの項目:GRADE(学年)、CLASS(組)、NUM(出席番号)の値と一致(赤枠部分)するデータを取得し、
取得用内部テーブル:T_DATAに格納します。(緑枠部分)


■ 出力結果

SELECT(FOR ALL ENTRIES – 新構文)

SELECT (FOR ALL ENTRIES)を新構文で記述すると以下の通りです。
  新構文では、内部テーブルをJOINすることで検索用内部テーブルと値が一致するデータを取得します。
また、取得項目は明示的に記述し、内部テーブルには別名が必須です。
旧構文との大きな違いは、内部結合を用いるため 検索用内部テーブルが0件の場合でも自動で全件選択になることはありません。


「サンプルコード」②:FOR ALL ENTRIES 新構文=内部テーブルJOIN
EPORT ZCUSLIST1_INTERNAL_ALL_NEW.

START-OF-SELECTION.

*(1) 生徒情報マスタ(ZTEST_TBL_001)から以下のデータを内部テーブルへ読み込みます。
  SELECT *
    FROM ZTEST_TBL_001 AS M
    WHERE M~GRADE = 1
      AND M~CLASS = 'A'
      AND M~NUM   <= 2
    INTO TABLE @DATA(LT_INDEX).

*(2) (1)で用意した内部テーブルを検索用内部テーブルとして、
*   生徒別テスト得点情報テーブル(ZTEST_TBL_002)の読み込みを行います。
*   以下の生徒別テスト得点情報テーブルデータから、
*   検索用内部テーブルと検索キー項目(grade,class,num)が一致するデータを読み込みます。
  "新構文(内部結合)では、検索用が0件なら結果も0件(全件抽出にならない)
  SELECT
    D~GRADE,
    D~CLASS,
    D~NUM,
    D~TESTNAME,
    D~JAPANESE,
    D~ENGLISH,
    D~MATH
    FROM ZTEST_TBL_002 AS D
    INNER JOIN @LT_INDEX AS I
      ON  D~GRADE = I~GRADE
      AND D~CLASS = I~CLASS
      AND D~NUM   = I~NUM
    INTO TABLE @DATA(LT_DATA).
*ヘッダの帳票出力
  WRITE: 001(4)  '学年',
         010(2)  '組',
         020(8)  '出席番号',
         030(10) 'テスト名',
         050(4)  '国語',
         055(4)  '英語',
         060(4)  '数学'
        ULINE.

  LOOP AT LT_DATA INTO DATA(LS_DATA).
    WRITE: /001(1)  LS_DATA-GRADE,
            010(2)  LS_DATA-CLASS,
            020(2)  LS_DATA-NUM,
            030(10) LS_DATA-TESTNAME,
            050(3)  LS_DATA-JAPANESE,
            055(3)  LS_DATA-ENGLISH,
            060(3)  LS_DATA-MATH.
    NEW-LINE.
  ENDLOOP. 
END-OF-SELECTION.


実行結果

NEXT>> 第18章 出力とメッセージ

研修の詳細はこちら