💭

SQL JOIN大全:内部結合・外部結合・USING句・NATURAL JOIN・自己結合をまとめて解説

に公開

内部結合・外部結合・完全外部結合 — 3つのJOINを視覚的に理解する

SQL の JOIN は、複数の表にまたがるデータを結び付けて 1 つの結果として取得するための仕組みです。
特に「共通する列(キー)」を基準に表どうしを関連付けることで、単一の表では得られない情報を取り出すことができます。

JOIN には 内部結合(INNER JOIN)、
片側の表をすべて残す 外部結合(OUTER JOIN)、
両方の表の全件を対象にする 完全外部結合(FULL OUTER JOIN) などがあり、
取得する行の範囲がそれぞれ異なります。
PROD表とCATEGORY表の構造とデータを確認して下さい。
PROD表

列名 NULL?
PRODID NOT NULL NUMBER(2)
NAME VARCHAR2(20)
CATEGORY NUMBER(2)
STARTDATE DATE
ENDDATE DATE
  PROD_ID PROD_NAME            CATEGORY_ID START_DAT END_DATE
---------- -------------------- ----------- --------- ---------
        10 APPLE                          1 01-JAN-24
        11 TV                             2 01-OCT-23 01-MAR-24
        12 NO_CATEGORY

CATEGORY表(カテゴリ)

列名 NULL?
CATEGORY NOT NULL NUMBER(2)
NAME VARCHAR2(20)
CATEGORY_ID CATEGORY_NAME
----------- --------------------
          1 FOOD
          2 ELECTRONICS

LEFT OUTER(PRODをすべて出す)

LEFT OUTER JOIN は、左側の表(ここでは PROD)を基準にします。
そのため PROD の行はすべて結果に含まれます。
結合条件である CATEGORY_ID が CATEGORY 表に存在する場合は対応する行が結合され、
存在しない場合は CATEGORY 側の列が NULL になります。

SELECT *
FROM PROD P
LEFT JOIN CATEGORY C
    ON P.CATEGORY_ID = C.CATEGORY_ID;

実行結果

PROD_ID  PROD_NAME     CATEGORY_ID  START_DATE  END_DATE    CATEGORY_ID  CATEGORY_NAME
-------  ------------  -----------  ----------  ----------  -----------  ----------------
10       APPLE         1            01-JAN-24               1            FOOD
11       TV            2            01-OCT-23   01-MAR-24   2            ELECTRONICS
12       NO_CATEGORY                (NULL)       (NULL)     (NULL)       (NULL)

RIGHT OUTER(CATEGORYをすべて出す)

RIGHT OUTER JOIN は、右側の表(今回では CATEGORY)を基準にします。
そのため CATEGORY の行はすべて結果に含まれます。
PROD の CATEGORY_ID と一致する行があれば結合され、
一致しない場合は PROD 側の列が NULL になります。

SELECT *
FROM PROD P
RIGHT OUTER JOIN CATEGORY C
    ON P.CATEGORY_ID = C.CATEGORY_ID;

実行結果

PROD_ID   PROD_NAME     CATEGORY_ID   START_DATE   END_DATE     CATEGORY_ID   CATEGORY_NAME
--------  ------------  ------------  -----------  -----------  ------------  ----------------
10        APPLE         1             01-JAN-24                   1            FOOD
11        TV            2             01-OCT-23     01-MAR-24     2            ELECTRONICS


INNER JOIN

内部結合(INNER JOIN)は、結合条件が一致する行だけを取得する結合です。
両方の表に共通して存在するデータだけが結果に含まれ、一致しない行は結果に含まれません。

SELECT *
FROM PROD P
INNER JOIN CATEGORY C
    ON P.CATEGORY_ID = C.CATEGORY_ID;

実行結果

PROD_ID   PROD_NAME     CATEGORY_ID   START_DATE   END_DATE     CATEGORY_ID   CATEGORY_NAME
--------  ------------  ------------  -----------  -----------  ------------  ----------------
10        APPLE         1             01-JAN-24                   1            FOOD
11        TV            2             01-OCT-23     01-MAR-24     2            ELECTRONICS

FULL OUTER JOIN

完全外部結合(FULL OUTER JOIN)は、左表と右表のどちらにも存在するすべての行を結果に含める結合です。
PROD と CATEGORY の両方を基準にするため、結合できる行は結合され、
どちらか片方にしか存在しない行も NULL を補ってすべて返されます。

SELECT *
FROM PROD P
FULL OUTER JOIN CATEGORY C
    ON P.CATEGORY_ID = C.CATEGORY_ID;

PROD_ID   PROD_NAME     CATEGORY_ID   START_DATE   END_DATE    CATEGORY_ID   CATEGORY_NAME
--------  ------------  ------------  -----------  ----------  ------------  ----------------
10        APPLE         1             01-JAN-24                   1            FOOD
11        TV            2             01-OCT-23     01-MAR-24     2            ELECTRONICS
12        NO_CATEGORY                (NULL)       (NULL)        (NULL)        (NULL)

今の CATEGORY 表には

「PROD に存在しない CATEGORY」が無いため、
右側 NULL 行は出ていません。

例:CATEGORY に 3 BOOK を入れるとこういう行が出ます↓

NULL      NULL        NULL          NULL         NULL          3         BOOK

USING 句とは?

JOIN の基本的な仕組みを理解した後は、
結合条件の書き方の違いにも触れておく必要があります。
SQL では、結合条件の指定方法として ON句 と USING句 の2種類があります。

通常は ON A.col = B.col のように結合条件を明示しますが、
両方の表に「同じ名前」の列が存在する場合、
USING句を使うことでより簡潔に書くことができます。

同じ名前の列で等価結合(=)したいときに使う JOIN の構文

SELECT *
FROM A
JOIN B USING (dept_id);

🔽USING に指定した列はテーブル別名を付けて参照できない
以下は エラーになります:

SELECT A.dept_id FROM A JOIN B USING (dept_id);

USINGは「両方のテーブルに共通の列」として扱うため
別名を付けて参照すると不正扱いになります。

🔽ON句を用いた結合文とは結果が異なる
以下の、A表B表があります。ON句とUSING句を用いた結合結果の違いを見ていきましょう。
A表

    EMP_ID    DEPT_ID NAME
---------- ---------- --------------------
         1         10 AAA
         2         10 BBB

B表

   DEPT_ID     SALARY
---------- ----------
        10     500000

USING句を用いた結合

SELECT *
FROM A
JOIN B USING (dept_id);

実行結果

   DEPT_ID     EMP_ID NAME                     SALARY
---------- ---------- -------------------- ----------
        10          1 AAA                      500000
        10          2 BBB                      500000

ON句を用いた結合

SELECT *
FROM A
JOIN B ON A.dept_id = B.dept_id;

実行結果

    EMP_ID    DEPT_ID NAME                    DEPT_ID     SALARY
---------- ---------- -------------------- ---------- ----------
         1         10 AAA                          10     500000
         2         10 BBB                          10     500000

違いまとめ

項目 ON句 USING句
表示される列数 dept_id が2つ表示 dept_id が1つに統合
別名で結合列を指定 OK NG
同名列が必要か 必要なし 同じ名前の列がないと使えない

自然結合(NATURAL JOIN)とは

USING句の次によく比較されるのが 自然結合(NATURAL JOIN) です。
自然結合は、両方の表にある同名列を自動で結合条件にする仕組みです。
同名の列すべてを自動で結合条件にして等価結合を行うJOIN のことです。
基本構文

SELECT *
FROM A NATURAL JOIN B;

A表

COLUMN_NAME DATA_TYPE
EMP_ID NUMBER
DEPT_ID NUMBER
NAME VARCHAR2
EMP_ID DEPT_ID NAME
1 10 AAA
1 20 AAA
2 10 BBB
SELECT column_name, data_type, data_length
FROM user_tab_columns
WHERE table_name = 'A'
ORDER BY column_id;

COLUMN_NAME DATA_TYPE DATA_LENGTH
EMP_ID NUMBER 22
DEPT_ID NUMBER 22
NAME VARCHAR2 20
SELECT * FROM B ORDER BY emp_id, dept_id;
EMP_ID DEPT_ID SALARY
1 10 500000
1 20 600000
2 10 700000
SELECT column_name, data_type, data_length
FROM user_tab_columns
WHERE table_name = 'B'
ORDER BY column_id;

COLUMN_NAME DATA_TYPE DATA_LENGTH
EMP_ID NUMBER 22
DEPT_ID NUMBER 22
SALARY NUMBER 22

AとBの共通列は

EMP_ID

DEPT_ID

🔹 両方のデータ型も完全一致(NUMBER)

→ 自然結合の対象列はこの2つになる

SELECT *
FROM A JOIN B
  ON A.emp_id = B.emp_id
 AND A.dept_id = B.dept_id;


    EMP_ID    DEPT_ID NAME                     EMP_ID    DEPT_ID     SALARY
---------- ---------- -------------------- ---------- ---------- ----------
         1         10 AAA                           1         10     500000
         1         20 AAA                           1         20     600000
         2         10 BBB                           2         10     700000

2つの表に同名で異なるデータ型の列がある場合、自然結合を行うとエラーとなります。
※ データ型が異なる場合でも、Oracleの暗黙的なデータ型変換が可能な場合にはエラーなく実行できます。

SQL> DESC departments;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------    
 DEPARTMENT_ID                             NOT NULL NUMBER
 DEPARTMENT_NAME                                    VARCHAR2(50)

SQL> DESC departments2;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------    
 DEPARTMENT_ID                             NOT NULL ROWID
 DEPARTMENT_NAME                                    VARCHAR2(50)

SQL> SELECT department_name
FROM departments NATURAL JO  2  IN departments2;
SELECT department_name
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected NUMBER got ROWID

自己結合とは

JOIN は通常、複数の表を結合するときに使用しますが、
実は 1つの表の中だけでも結合を行う場合 があります

同じ表を2つに見立てて、
A表とB表のように扱って結合します。

よくある問い
EMPLOYEE_IDが「1008」の従業員と上司が同じである従業員の名前と、上司のEMPLOYEE_IDを出力します。
どのような問合せを使用するか考えてみましょう

SQL> select * from employees;

EMPLOYEE_ID EMPLOYEE_NAME                                      MANAGER_ID
----------- -------------------------------------------------- ----------
       1001 BOSS
       1010 TOM                                                      1001
       1008 MIKE                                                     1010
       1011 JOHN                                                     1010
       1020 SARA                                                     1010
       2000 ALLEN                                                    9999

6 rows selected.
SELECT m.employee_name, e.manager_id
FROM employees e
RIGHT OUTER JOIN employees m
  ON e.manager_id = m.manager_id
WHERE e.employee_id = 1008;

WHERE e.employee_id = 1008
→ e を MIKE の1行に固定する

JOIN 条件 e.manager_id = m.manager_id
→ MIKE の上司ID(1010)と同じ manager_id を持つ人を m 側に全部並べる

RIGHT JOIN なので m 側の行を全部残す
→ MIKE / JOHN / SARA の3行が残る

横に並べると以下になる

image.png

よくある間違いの選択肢

SELECT e.employee_name, e.manager_id FROM employees e
LEFT OUTER JOIN employees m on (e.employee_id = m.employee_id)
WHERE e.employee_id = 1008;
EMPLOYEE_NAME                                      MANAGER_ID
-------------------------------------------------- ----------
MIKE                                                     1001

なぜ「JOHN」「SARA」が出ない

JOIN条件が「e.manager_id = m.employee_id」なので

→ 上司 TOM(employee_id=1010)1件だけ がヒットします

Discussion