Oracle SQL 3つ以上の表の結合と非等価結合の正しい書き方

Oracle Master Silver

業務システムの開発やデータ分析では、1つのテーブルだけでなく「3つ以上の表」を組み合わせて情報を取得するシーンが頻繁にあります。また、単純なイコール(=)だけでなく、範囲条件などを使った「非等価結合」を使いこなせると、SQLの表現力が格段に向上します。

この記事では、Oracle SQL における複数テーブルの結合手順と、実務で役立つ非等価結合の使い方を、実機で動くサンプルコード付きで解説します。

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

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

結論:複数テーブル結合のポイント

  • 3つ以上の結合FROM 句の後ろに JOIN を続けて記述するだけで拡張可能。
  • 非等価結合:結合条件に = 以外(BETWEEN, <, > など)を使用する結合。
  • コツ:まずは「主となるテーブル」を決め、そこに関連するマスタを1つずつ JOIN していく。
  • 注意点:結合条件を忘れると「直積(クロス結合)」になり、データが膨れ上がるため注意が必要。

結合(JOIN)の基礎知識

表の結合とは

リレーショナルデータベースにおいて、正規化(分割)された複数のテーブルを、共通の列や条件に基づいて横に繋げる操作を指します。Oracle Databaseでは主に ANSI構文(INNER JOIN, LEFT JOIN など)が推奨されます。

非等価結合(Non-Equi Join)とは

通常、結合条件には ON A.ID = B.ID のように等価演算子(=)を使いますが、等価演算子以外を使って結合することを「非等価結合」と呼びます。 代表例が「給与がこの範囲内なら、このグレード」といった範囲結合(BETWEEN) です。

前提データ(検証用DDL/DML)

以下のSQLを実行して、検証用のテーブルとデータを作成してください。 ※ EMP(社員)、DEPT(部署)、SALGRADE(給与等級)の3つを使用します。

-- Drop tables if they exist
-- DROP TABLE EMP;
-- DROP TABLE DEPT;
-- DROP TABLE SALGRADE;

-- 1. Create DEPT table
CREATE TABLE DEPT (
    DEPTNO NUMBER(2) PRIMARY KEY,
    DNAME  VARCHAR2(14),
    LOC    VARCHAR2(13)
);

INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT VALUES (20, 'RESEARCH',   'DALLAS');
INSERT INTO DEPT VALUES (30, 'SALES',      'CHICAGO');

-- 2. Create EMP table
CREATE TABLE EMP (
    EMPNO    NUMBER(4) PRIMARY KEY,
    ENAME    VARCHAR2(10),
    JOB      VARCHAR2(9),
    MGR      NUMBER(4),
    HIREDATE DATE,
    SAL      NUMBER(7, 2),
    COMM     NUMBER(7, 2),
    DEPTNO   NUMBER(2)
);

INSERT INTO EMP VALUES (7369, 'SMITH', 'CLERK',    7902, TO_DATE('1980-12-17', 'YYYY-MM-DD'), 800,  NULL, 20);
INSERT INTO EMP VALUES (7499, 'ALLEN', 'SALESMAN', 7698, TO_DATE('1981-02-20', 'YYYY-MM-DD'), 1600, 300,  30);

-- 3. Create SALGRADE table
CREATE TABLE SALGRADE (
    GRADE NUMBER(1),
    LOSAL NUMBER(7),
    HISAL NUMBER(7)
);

INSERT INTO SALGRADE VALUES (1, 700,  1200);
INSERT INTO SALGRADE VALUES (2, 1201, 1400);
INSERT INTO SALGRADE VALUES (3, 1401, 2000);
INSERT INTO SALGRADE VALUES (4, 2001, 3000);

COMMIT;

実践1:3つ以上の表を結合する

社員情報(EMP)に対し、部署名(DEPT)と給与グレード(SALGRADE)を同時に紐付けて、1つのレポートを作成します。

結合のイメージ

  1. EMP + DEPT:部署番号 (DEPTNO) が一致するものを結合(等価結合
  2. EMP + SALGRADE:給与 (SAL) が等級の下限 (LOSAL) と上限 (HISAL) の間にあるものを結合(非等価結合

これらを1つのクエリにまとめます。

SELECT 
    E.ENAME AS Employee_Name, 
    E.JOB   AS Job_Title, 
    D.DNAME AS Dept_Name, 
    E.SAL   AS Salary,
    S.GRADE AS Salary_Grade
FROM 
    EMP E
-- Join 1: DEPT table (Equi-join)
INNER JOIN 
    DEPT D ON E.DEPTNO = D.DEPTNO
-- Join 2: SALGRADE table (Non-equi-join)
INNER JOIN 
    SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL;

実行結果:

SQL> SELECT
2 E.ENAME AS Employee_Name,
3 E.JOB AS Job_Title,
4 D.DNAME AS Dept_Name,
5 E.SAL AS Salary,
6 S.GRADE AS Salary_Grade
7 FROM
8 EMP E
9 INNER JOIN
10 DEPT D ON E.DEPTNO = D.DEPTNO
11 INNER JOIN
12 SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL;

EMPLOYEE_N JOB_TITLE DEPT_NAME SALARY SALARY_GRADE
---------- --------- -------------- ---------- ------------
SMITH CLERK RESEARCH 800 1
ALLEN SALESMAN SALES 1600 3

解説

JOIN 句を連続して書くことで、3つでも4つでもテーブルを結合できます。Oracle Databaseのオプティマイザが最適な結合順序(駆動表など)を自動的に判断して実行します。

実践2:非等価結合(Non-Equi Join)の仕組み

先ほどの例でさりげなく登場した以下の部分が、非等価結合の実装です。

JOIN SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL

なぜ非等価結合が必要なのか?

SALGRADE 表には、EMP 表と一致する共通のID列がありません。代わりに「700〜1200の間ならグレード1」という範囲の情報を持っています。 このような場合、= では結合できないため、BETWEEN や不等号(>=, <=)を使って「値が範囲内に収まる行」を探し出して結合します。

参考:CASE式による代用(結合しない方法)

元記事では CASE 文による分類も紹介されていました。テーブル結合をせず、SQLの中で固定的に判定を行う場合は以下のように書きます。

-- Use CASE expression instead of JOIN
SELECT 
    E.ENAME, 
    E.SAL,
    CASE 
        WHEN E.SAL > 2000 THEN 'HIGH'
        WHEN E.SAL BETWEEN 1000 AND 2000 THEN 'MEDIUM'
        ELSE 'LOW'
    END AS SALARY_CATEGORY
FROM 
    EMP E;

実行結果:

SQL> SELECT
2 E.ENAME,
3 E.SAL,
4 CASE
5 WHEN E.SAL > 2000 THEN 'HIGH'
6 WHEN E.SAL BETWEEN 1000 AND 2000 THEN 'MEDIUM'
7 ELSE 'LOW'
8 END AS SALARY_CATEGORY
9 FROM
10 EMP E;

ENAME SAL SALARY
---------- ---------- ------
SMITH 800 LOW
ALLEN 1600 MEDIUM

使い分け:

  • 非等価結合: 閾値やランク定義がマスタテーブル(SALGRADEなど)で管理されており、頻繁に変更される可能性がある場合。
  • CASE式: 分析クエリなどで一時的にデータを分類したい場合や、マスタテーブルが存在しない場合。

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

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

応用:データベースリンク(DB Link)先との結合

Oracle Databaseでは、ネットワーク越しにある別のデータベースのテーブルとも結合が可能です。これを実現するのが「データベースリンク(DB Link)」機能です。

データベースリンク(DB Link)とは

現在接続しているデータベースから、リモートにある別のデータベースへアクセスするための定義オブジェクトです。 テーブル名の後ろに @リンク名 を付けることで、まるでローカルにあるテーブルのように扱うことができます。

実行例:ローカルのEMP表とリモートのDEPT表を結合

例えば、本社のDB(ローカル)にある社員テーブルと、支社のDB(リモート:TOKYO_HQ)にある部署テーブルを結合する場合、以下のように記述します。

SELECT 
    E.ENAME, 
    D.DNAME
FROM 
    EMP E
-- Join with remote DEPT table via DB Link
INNER JOIN 
    DEPT@TOKYO_HQ D ON E.DEPTNO = D.DEPTNO;

注意点

  • パフォーマンス: ネットワークを経由するため、大量データを結合すると処理が極端に遅くなる可能性があります。
  • Driving Site: 通常、実行した側のDBにデータを集めて結合処理が行われます。リモート側のデータ量が膨大な場合は、DRIVING_SITE ヒントなどでチューニングが必要になることがあります。

トラブルシューティング

結合操作でよく発生するエラーとその対処法です。

エラーコードエラーメッセージ例原因と対処
ORA-00918column ambiguously defined結合した複数の表に同じ列名(例:DEPTNO)があり、どちらを指しているか不明確。E.DEPTNO のように表別名を付けて指定する。
ORA-00904invalid identifier列名のタイプミス、またはテーブル別名(エイリアス)を付けたのに、元のテーブル名で列を指定している(例:FROM EMP E としたのに EMP.ENAME と書いた)。
データ過多(エラーなしで大量行が返る)結合条件漏れによる「直積(クロス結合)」の可能性。ON 句が正しく記述されているか確認する。

運用・パフォーマンス上の注意

  1. インデックスの活用
    • 結合キー(DEPTNOなど)にインデックスがない場合、テーブル全体を読み込む「Full Table Scan」が発生し、結合処理(Hash Joinなど)が遅くなる可能性があります。外部キー列にはインデックスを作成するのが定石です。
  2. 非等価結合のコスト
    • BETWEEN や不等号による結合は、等価結合に比べて処理コストが高くなりがちです。数百万件以上の大規模テーブル同士を非等価結合する場合は、処理時間に注意してください。
  3. ANSI構文の推奨
    • 古いOracle独自の結合構文(WHERE E.DEPTNO = D.DEPTNO)よりも、JOIN ... ON ... 形式の使用を推奨します。結合条件とフィルタ条件(WHERE句)が明確に分離され、可読性が向上しミスが減ります。

FAQ:よくある質問

Q1. 4つ以上のテーブルも結合できますか?

A. はい、可能です。JOIN 句をさらに追加していくだけです。ただし、結合数が増えるほどSQLが複雑になり、パフォーマンスチューニングが難しくなる傾向があります。

Q2. 結合してもデータが表示されない行があります。なぜですか?

A. INNER JOIN(内部結合)を使用しているためです。内部結合は「両方のテーブルに条件が合致するデータがある場合」のみ表示します。 部署に所属していない社員や、グレード範囲外の給与の社員も表示したい場合は、LEFT JOIN(外部結合)を使用してください。

Q3. 結合の順序はパフォーマンスに影響しますか?

A. Oracleのオプティマイザ(CBO)が優秀なため、基本的に書く順番(FROM A JOIN BFROM B JOIN A か)は実行計画に影響しません。人間が読んで分かりやすい順序(主テーブル → 関連マスタ)で記述しましょう。

まとめ

  • 3つ以上の結合は、焦らず1つずつ JOIN を追加していけばOK。
  • 非等価結合は、マスタテーブルとID以外(範囲など)で紐付けたいときに強力な武器になる。
  • DB Link結合を使えば、別DBのテーブルとも透過的に結合できる(ただしネットワーク負荷に注意)。
  • 列名が重複する場合は必ず表別名(エイリアス) を明記して、エラー「ORA-00918」を防ぐ。

実務では「EMP表のようなトランザクション」と「DEPT表のようなマスタ」を正しく結合できるかが基本となります。ぜひ今回のサンプルコードを手元の環境で動かして、結合の挙動を確認してみてください。

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



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

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

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

コメント

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