Oracle SQL 表結合(JOIN)完全解説!図解とサンプルで習得

Oracle Master Silver

データベースを扱う上で避けては通れないのが「表の結合(JOIN)」です。

「内部結合と外部結合、どちらを使えばどのような結果になるのか?」「データが消えたり増えたりする論理的な理由は?」といった疑問は、適切なサンプルデータで検証することで明確になります。

本記事では、結合の挙動を理解しやすい独自の検証用テーブル(データはすべて英語)を作成し、Oracle Database における結合(INNER, LEFT/RIGHT, FULL, SELF)の仕組みを徹底解説します。英語環境やグローバルプロジェクトでも通用する基礎知識を固めましょう。

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?


結論・結合パターンの一覧

まずは各結合の論理的な動作イメージを掴んでください。

結合タイプキーワード動作の概要NULLの扱い
内部結合INNER JOIN積集合 (Intersection)結合条件が一致する行のみを取得します。一致しない行は除外されます。
左外部結合LEFT JOIN左表優先 (Left Outer)左表の全行を取得し、右表からは一致する行のみ結合します。不一致部分は NULL。
右外部結合RIGHT JOIN右表優先 (Right Outer)右表の全行を取得し、左表からは一致する行のみ結合します。不一致部分は NULL。
完全外部結合FULL JOIN和集合 (Union)左右どちらかの表に存在すれば行を取得します。不一致部分は NULL。
自己結合(同じ表)再帰的結合同一テーブル内で結合を行います(上司・部下など階層構造の表現)。
クロス結合CROSS JOIN直積 (Cartesian Product)全行 × 全行のすべての組み合わせを生成します。

【この記事でわかること】

  • 結合の基本構文(FROM, JOIN, ON)の構造的解説
  • INNEROUTER における取得行数の違い
  • 独自のサンプルテーブル(英語データ)を使った実機検証

1. 初心者向け:結合の基本構文を解剖する

Oracle 9i 以降、標準的に採用されている ANSI結合構文JOIN キーワードを使う書き方)を解説します。

基本構文テンプレート

SELECT
    A.Column_Name,
    B.Column_Name
FROM
    Table_A  Alias_A
    【結合の種類】 JOIN Table_B  Alias_B
    ON A.Key_Column = B.Key_Column;

構文の3つのポイント

  1. FROM 句と JOIN 句:
    • FROM には「主」となるテーブル(左側)を記述します。
    • JOIN には「結合」したいテーブル(右側)を記述します。
  2. ON 句(結合条件):
    • 2つのテーブルを関連付ける条件列を指定します。
    • 例: ON EMP.DEPT_ID = DEPT.DEPT_ID
  3. テーブル別名(エイリアス):
    • FROM EMP_SAMPLE E のように短い別名(Eなど)を定義するのが一般的です。
    • 列指定時に E.NAME と記述することで、どのテーブルの列かを明確にします。

2. 検証用データの作成

SCOTTスキーマでは結合不一致(NULL)の挙動が見えにくいため、今回は以下の特徴を持つ独自のテーブルを作成します。英語記事への展開も考慮し、データ内容はすべてシングルバイト(英語)で構成します。

  • DEPT_SAMPLE (部署): 4つの部署。うち1つは社員がいない部署 (HR)。
  • EMP_SAMPLE (社員): 5人の社員。うち1人は部署未定の社員 (Martin)。

ご自身の環境で以下のSQLを実行してください。

-- 1. Create Department Table (DEPT_SAMPLE)
CREATE TABLE DEPT_SAMPLE (
    DEPT_ID   NUMBER PRIMARY KEY,
    DEPT_NAME VARCHAR2(20)
);

INSERT INTO DEPT_SAMPLE VALUES (10, 'Sales');       -- Has employees
INSERT INTO DEPT_SAMPLE VALUES (20, 'Research');    -- Has employees
INSERT INTO DEPT_SAMPLE VALUES (30, 'Accounting');  -- Has employees
INSERT INTO DEPT_SAMPLE VALUES (40, 'HR');          -- No employees (No match in EMP)
COMMIT;

-- 2. Create Employee Table (EMP_SAMPLE)
CREATE TABLE EMP_SAMPLE (
    EMP_ID    NUMBER PRIMARY KEY,
    EMP_NAME  VARCHAR2(20),
    DEPT_ID   NUMBER,
    BOSS_ID   NUMBER
);

INSERT INTO EMP_SAMPLE VALUES (1, 'Smith',  10, NULL); -- Sales, No Boss
INSERT INTO EMP_SAMPLE VALUES (2, 'Allen',  10, 1);    -- Sales, Boss is Smith
INSERT INTO EMP_SAMPLE VALUES (3, 'Ward',   20, 1);    -- Research, Boss is Smith
INSERT INTO EMP_SAMPLE VALUES (4, 'Jones',  30, 2);    -- Accounting, Boss is Allen
INSERT INTO EMP_SAMPLE VALUES (5, 'Martin', NULL, 1);  -- No Dept (NULL), Boss is Smith
COMMIT;
SQL> SELECT * FROM DEPT_SAMPLE;

DEPT_ID DEPT_NAME
---------- --------------------
10 Sales
20 Research
30 Accounting
40 HR

SQL> SELECT * FROM EMP_SAMPLE;

EMP_ID EMP_NAME DEPT_ID BOSS_ID
---------- -------------------- ---------- ----------
1 Smith 10 (null)
2 Allen 10 1
3 Ward 20 1
4 Jones 30 2
5 Martin (null) 1

※本記事では SQL> set null (null) を実行して null を出力しています。


3. 内部結合(INNER JOIN)

お互いに結合キー(DEPT_ID)が一致するデータのみを取得します。

結合相手が存在しない行は、検索結果から除外されます。

SELECT 
    E.EMP_NAME, 
    D.DEPT_NAME
FROM EMP_SAMPLE E
INNER JOIN DEPT_SAMPLE D
ON E.DEPT_ID = D.DEPT_ID;

実行結果:

SQL> SELECT
2 E.EMP_NAME,
3 D.DEPT_NAME
4 FROM EMP_SAMPLE E
5 INNER JOIN DEPT_SAMPLE D
6 ON E.DEPT_ID = D.DEPT_ID;

EMP_NAME DEPT_NAME
-------------------- --------------------
Smith Sales
Allen Sales
Ward Research
Jones Accounting

解説:

  • 部署未定の Martin (DEPT_ID is NULL) は除外されました。
  • 所属社員がいない HR (DEPT_ID = 40) も除外されました。
  • これが INNER JOIN の特徴(厳格な一致)です。

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?


4. 外部結合(LEFT / RIGHT JOIN)

「一致しなくても、基準となるテーブルの行は維持したい」場合に使用します。

4-1. 左外部結合(LEFT JOIN)

FROM 句にあるテーブル(左側)の全行を表示します。

-- Main Table: EMP_SAMPLE (Left)
SELECT 
    E.EMP_NAME, 
    D.DEPT_NAME
FROM EMP_SAMPLE E          -- Left Side (Preserve all rows)
LEFT JOIN DEPT_SAMPLE D    -- Right Side
ON E.DEPT_ID = D.DEPT_ID;

実行結果:

SQL> SELECT
2 E.EMP_NAME,
3 D.DEPT_NAME
4 FROM EMP_SAMPLE E
5 LEFT JOIN DEPT_SAMPLE D
6 ON E.DEPT_ID = D.DEPT_ID;

EMP_NAME DEPT_NAME
-------------------- --------------------
Smith Sales
Allen Sales
Ward Research
Jones Accounting
Martin (null)

解説:

  • INNER JOIN で除外された Martin が表示されます。
  • 対応する DEPT_NAME が存在しないため、値は (null) となります。

4-2. 右外部結合(RIGHT JOIN)

JOIN 句にあるテーブル(右側)の全行を表示します。

-- Main Table: DEPT_SAMPLE (Right)
SELECT 
    E.EMP_NAME, 
    D.DEPT_NAME
FROM EMP_SAMPLE E          -- Left Side
RIGHT JOIN DEPT_SAMPLE D   -- Right Side (Preserve all rows)
ON E.DEPT_ID = D.DEPT_ID;

実行結果:

SQL> SELECT
2 E.EMP_NAME,
3 D.DEPT_NAME
4 FROM EMP_SAMPLE E
5 RIGHT JOIN DEPT_SAMPLE D
6 ON E.DEPT_ID = D.DEPT_ID;

EMP_NAME DEPT_NAME
-------------------- --------------------
Smith Sales
Allen Sales
Ward Research
Jones Accounting
(null) HR

解説:

  • 今度は HR 部門が表示されます。
  • 所属する EMP_NAME が存在しないため、値は (null) となります。

5. 完全外部結合(FULL JOIN)

左右どちらかのテーブルに行が存在すれば、すべて結果に表示します。

SELECT E.EMP_NAME, D.DEPT_NAME
FROM EMP_SAMPLE E
FULL JOIN DEPT_SAMPLE D
ON E.DEPT_ID = D.DEPT_ID;

実行結果:

SQL> SELECT E.EMP_NAME, D.DEPT_NAME
2 FROM EMP_SAMPLE E
3 FULL JOIN DEPT_SAMPLE D
4 ON E.DEPT_ID = D.DEPT_ID;

EMP_NAME DEPT_NAME
-------------------- --------------------
Smith Sales
Allen Sales
Ward Research
Jones Accounting
Martin (null)
(null) HR
  • Martin (部署なし) も HR (社員なし) も両方表示されます。
  • データの不整合チェックや、マスタ間の差分抽出(Diff)によく使用されます。

6. 自己結合(SELF JOIN)

同一テーブル同士を結合します。

EMP_SAMPLE テーブルには BOSS_ID(上司のID)があり、これは同テーブル内の EMP_ID を参照しています。これを使って「部下と上司」のリストを作成します。

SELECT 
    Worker.EMP_NAME  AS WORKER_NAME, 
    Boss.EMP_NAME    AS BOSS_NAME
FROM EMP_SAMPLE Worker        -- 1st instance (As Worker)
LEFT JOIN EMP_SAMPLE Boss     -- 2nd instance (As Boss)
ON Worker.BOSS_ID = Boss.EMP_ID;

実行結果:

SQL> SELECT
2 Worker.EMP_NAME AS WORKER_NAME,
3 Boss.EMP_NAME AS BOSS_NAME
4 FROM EMP_SAMPLE Worker
5 LEFT JOIN EMP_SAMPLE Boss
6 ON Worker.BOSS_ID = Boss.EMP_ID;

WORKER_NAME BOSS_NAME
-------------------- --------------------
Allen Smith
Ward Smith
Martin Smith
Jones Allen
Smith (null)

解説:

  • LEFT JOIN を使用することが重要です。INNER JOIN を使用した場合、上司がいない(BOSS_IDがNULLの)Smith が結果から消えてしまうためです。
  • 同一テーブルを使用するため、必ず Worker, Boss といった別名を定義して区別します。

7. クロス結合(CROSS JOIN)

結合条件を指定せず、すべての組み合わせ(直積)を生成します。

SELECT E.EMP_NAME, D.DEPT_NAME
FROM EMP_SAMPLE E
CROSS JOIN DEPT_SAMPLE D;

実行結果:

SQL> SELECT E.EMP_NAME, D.DEPT_NAME
2 FROM EMP_SAMPLE E
3 CROSS JOIN DEPT_SAMPLE D;

EMP_NAME DEPT_NAME
-------------------- --------------------
Smith Sales
Allen Sales
Ward Sales
Jones Sales
Martin Sales
Smith Research
Allen Research
Ward Research
Jones Research
Martin Research
Smith Accounting
Allen Accounting
Ward Accounting
Jones Accounting
Martin Accounting
Smith HR
Allen HR
Ward HR
Jones HR
Martin HR

20 rows selected.

結果: 5 Employees × 4 Departments = 20 Rows

テストデータの大量生成や、全パターンを網羅したマトリクス表の作成に使用します。


8. トラブルシューティングと注意点

結合条件の欠落(Cartesian Product)

ON 句を記述し忘れると、意図せず CROSS JOIN と同じ状態(直積)になり、行数が爆発的に増加します。これを「デカルト積」と呼び、パフォーマンス低下の主要因となります。

「(+)」演算子について

Oracle 独自の結合構文(例: WHERE E.DEPT_ID = D.DEPT_ID(+))は、LEFT JOIN と同等です。現在でも動作しますが、以下の理由から JOIN 句(ANSI構文)の使用が推奨されます。

  • 他のRDBMS(SQL Server, PostgreSQL等)との互換性がない
  • FULL JOIN が表現できない
  • 結合条件とフィルタ条件が混在し、可読性が低下する

まとめ

結合の種類SQL構文結果の特徴
内部結合INNER JOIN共通するデータのみ取得(Martin も HR も除外)
左外部結合LEFT JOIN左表を維持(Martin は残る、HR は除外)
右外部結合RIGHT JOIN右表を維持(Martin は除外、HR は残る)
完全外部結合FULL JOIN両方を維持(Martin も HR も残る)
自己結合(Same Table)上司・部下などの階層構造を表現

実務では 9割のケースで INNER JOIN か LEFT JOIN を使用します。
まずはこの2つの違いを、今回のサンプルデータで実際に動かして確認してください。

また、ローカルに存在するテーブルとDBLINKでリモートのDBに存在するテーブルを使用して結合を行うことも可能です。
DBLINKについては以下をご参照ください。

本記事は Oracle Database 19c を対象に解説します(他バージョンは画面や既定値が異なる場合があります)。


[参考]
Oracle Database SQL言語リファレンス 19c

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?

コメント

タイトルとURLをコピーしました