SQL を書く際、「条件に別の集計結果を使いたい」「2段階の処理を1回で済ませたい」と悩むことはありませんか?
Oracle 副問い合わせ(サブクエリ)を活用すると、複雑なデータ取得をシンプルに記述でき、アプリケーションのパフォーマンス向上にも繋がります。
この記事では、Oracle Database 19c を前提に、副問い合わせの基本構文から、相関副問い合わせ、EXISTS や WITH 句を使った実践的なテクニックまでを網羅的に解説します。
💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?
Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?
結論:副問い合わせ活用のポイント
時間がない方向けに、この記事の要点をリストアップしました。
- 副問い合わせとは:SQL 文の中に埋め込まれた「入れ子」の SELECT 文。
- 使い分け:
- スカラー:結果が「1行1列」。
=などで比較。 - 複数行:結果が「リスト」。
INで比較。 - 相関:外側の行ごとに評価される。重くなりやすいので注意。
- スカラー:結果が「1行1列」。
- ベストプラクティス:可読性と再利用性を高めるなら
WITH句 の利用を推奨。 - 注意点:
NOT INは NULL を含むと結果が空になる(NOT EXISTSを使う)。
1. 副問い合わせ(サブクエリ)の基礎
用語の定義と仕組み
副問い合わせ(Subquery)とは、メインの SQL 文(外側のクエリ)の中に記述される、括弧 () で囲まれた SELECT 文のことです。
SQL は通常、内側の副問い合わせから先に実行され、その結果を使って外側のメインクエリが実行されます(※相関副問い合わせを除く)。
- メインクエリ:親となる SQL(外側)
- サブクエリ:子となる SQL(内側)
2. 実習用サンプルデータの作成
解説と合わせて手元の環境で動作確認できるよう、サンプル表を作成します。
※SQL 内にマルチバイト文字(全角)を含まない形式で作成します。
前提条件
- 接続ユーザー:一般的な作成権限を持つユーザー(SCOTT 等)
- 環境:Oracle Database 19c Enterprise/Standard Edition 2
以下の SQL を実行して、EMPLOYEES(従業員)と DEPARTMENTS(部署)を作成してください。
-- テーブルが既に存在する場合は削除(任意)
-- DROP TABLE employees PURGE;
-- DROP TABLE departments PURGE;
-- 1. 部署表の作成
CREATE TABLE departments (
dept_id NUMBER PRIMARY KEY,
dept_name VARCHAR2(50)
);
-- 2. 従業員表の作成
CREATE TABLE employees (
emp_id NUMBER PRIMARY KEY,
emp_name VARCHAR2(50),
dept_id NUMBER,
salary NUMBER,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
-- 3. データ挿入
-- 部署: 1=Sales, 2=Dev, 3=HR, 4=Marketing(空の部署)
INSERT INTO departments VALUES (1, 'Sales');
INSERT INTO departments VALUES (2, 'Dev');
INSERT INTO departments VALUES (3, 'HR');
INSERT INTO departments VALUES (4, 'Marketing');
-- 従業員
INSERT INTO employees VALUES (1, 'Taro Tanaka', 1, 300000);
INSERT INTO employees VALUES (2, 'Jiro Suzuki', 2, 400000);
INSERT INTO employees VALUES (3, 'Saburo Sato', 1, 350000);
INSERT INTO employees VALUES (4, 'Shiro Yamada', 3, 500000);
COMMIT;
▼作成されたテーブル
SQL> SELECT * FROM departments;
DEPT_ID DEPT_NAME
---------- --------------------
1 Sales
2 Dev
3 HR
4 Marketing
SQL> SELECT * FROM employees;
EMP_ID EMP_NAME DEPT_ID SALARY
---------- -------------------- ---------- ----------
1 Taro Tanaka 1 300000
2 Jiro Suzuki 2 400000
3 Saburo Sato 1 350000
4 Shiro Yamada 3 500000
3. 実践:副問い合わせの種類と書き方
3-1. スカラー副問い合わせ(単一行)
副問い合わせの結果が「1行、かつ1列」であるパターンです。= < > などの比較演算子で利用します。
例:’Sales’ 部門(IDが不明)に所属する従業員を取得する
SELECT emp_name, salary
FROM employees
WHERE dept_id = (
SELECT dept_id
FROM departments
WHERE dept_name = 'Sales'
);
▼実行結果
SQL> SELECT emp_name, salary
2 FROM employees
3 WHERE dept_id = (
4 SELECT dept_id
5 FROM departments
6 WHERE dept_name = 'Sales'
7 );
EMP_NAME SALARY
-------------------------------------------------- ----------
Taro Tanaka 300000
Saburo Sato 350000
解説
カッコ内の SQL が先に実行され、結果の 1 が返ります。その後、メインクエリが WHERE dept_id = 1 として実行されます。
3-2. 複数行の副問い合わせ
副問い合わせの結果が「複数行」になるパターンです。= で繋ぐとエラーになるため、IN 演算子を使用します。
例:’Sales’ または ‘Dev’ 部門に所属する従業員を取得する
SELECT emp_name, salary
FROM employees
WHERE dept_id IN (
SELECT dept_id
FROM departments
WHERE dept_name IN ('Sales', 'Dev')
);
▼実行結果
SQL> SELECT emp_name, salary
2 FROM employees
3 WHERE dept_id IN (
4 SELECT dept_id
5 FROM departments
6 WHERE dept_name IN ('Sales', 'Dev')
7 );
EMP_NAME SALARY
-------------------------------------------------- ----------
Taro Tanaka 300000
Jiro Suzuki 400000
Saburo Sato 350000
実行結果のイメージ
サブクエリが 1 と 2 を返します。メインクエリは WHERE dept_id IN (1, 2) として評価されます。
3-3. 相関副問い合わせ
ここが初学者の壁になりやすいポイントです。
副問い合わせの中で、外側のテーブルの列を参照しているパターンです。
例:自分の部署の「平均給与」よりも高い給与をもらっている従業員を探す
SELECT e.emp_name, e.salary, e.dept_id
FROM employees e
WHERE e.salary > (
SELECT AVG(s.salary)
FROM employees s
WHERE s.dept_id = e.dept_id
);
▼実行結果
SQL> SELECT e.emp_name, e.salary, e.dept_id
2 FROM employees e
3 WHERE e.salary > (
4 SELECT AVG(s.salary)
5 FROM employees s
6 WHERE s.dept_id = e.dept_id
7 );
EMP_NAME SALARY DEPT_ID
-------------------------------------------------- ---------- ----------
Saburo Sato 350000 1
解説
- メインクエリが
employeesテーブル(e)から1行読み込みます(例:Taro Tanaka, Dept 1)。 - サブクエリが実行されます。この時
WHERE s.dept_id = e.dept_idのe.dept_idに1が代入されます。 - Dept 1 の平均給与(325,000)が計算されます。
- Taro の給与(300,000)と比較されます(300,000 > 325,000 は False)。
- 次の行へ進み、繰り返します。
💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?
Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?
4. 応用:EXISTS と WITH 句
4-1. EXISTS / NOT EXISTS(存在確認)
行のデータそのものではなく、「データが存在するかどうか」だけをチェックします。特に大量データを扱う際、IN よりも高速に動作する場合があります。
例:従業員が誰もいない部署を探す(NOT EXISTS)
SELECT d.dept_name
FROM departments d
WHERE NOT EXISTS (
SELECT 1
FROM employees e
WHERE e.dept_id = d.dept_id
);
▼実行結果
SQL> SELECT d.dept_name
2 FROM departments d
3 WHERE NOT EXISTS (
4 SELECT 1
5 FROM employees e
6 WHERE e.dept_id = d.dept_id
7 );
DEPT_NAME
--------------------------------------------------
Marketing
(Marketing 部門には誰も配属していないため)
4-2. WITH 句(Common Table Expression)
SQL が長くなる場合、副問い合わせを WITH 句として先頭に切り出すことで、可読性が劇的に向上します。現代の Oracle SQL 開発では推奨される書き方です。
例:各部署の平均給与を計算し、それより高い給与の従業員をリスト化(相関副問い合わせの書き換え)
-- 前処理:部署ごとの平均給与を定義
WITH dept_avg AS (
SELECT dept_id, AVG(salary) AS avg_sal
FROM employees
GROUP BY dept_id
)
-- メイン処理
SELECT e.emp_name, e.salary, a.avg_sal
FROM employees e
JOIN dept_avg a ON e.dept_id = a.dept_id
WHERE e.salary > a.avg_sal;
▼実行結果
SQL> WITH dept_avg AS (
2 SELECT dept_id, AVG(salary) AS avg_sal
3 FROM employees
4 GROUP BY dept_id
5 )
6 SELECT e.emp_name, e.salary, a.avg_sal
7 FROM employees e
8 JOIN dept_avg a ON e.dept_id = a.dept_id
9 WHERE e.salary > a.avg_sal;
EMP_NAME SALARY AVG_SAL
-------------------------------------------------- ---------- ----------
Saburo Sato 350000 325000
5. トラブルシューティング(よくあるエラー)
副問い合わせ利用時に頻出するエラーと対処法です。
| エラーコード | エラーメッセージ | 原因と対処 |
| ORA-01427 | 単一行副問合せにより2つ以上の行が戻されました | = で比較しているサブクエリが複数の値を返しています。IN に変えるか、サブクエリの条件を厳しくして1行に絞ってください。 |
| ORA-00904 | 無効な識別子です | サブクエリ内の列名が間違っているか、外側の列を参照する際のエイリアス(別名)指定が正しくありません。 |
| データなし | (結果が0件になる) | NOT IN の落とし穴です。サブクエリの結果に NULL が含まれると、全体の結果が空になります。NOT EXISTS を使用してください。 |
6. 運用・パフォーマンス上の注意点
メリットとデメリット
- メリット:複雑なロジックを1つの SQL で完結できる。アプリ側のコード行数を減らせる。
- デメリット:ネスト(入れ子)が深すぎると、第三者が読めない「スパゲッティコード」になる。
パフォーマンスの落とし穴
相関副問い合わせは「行数分だけサブクエリが実行」されるため、外側のテーブルが巨大な場合、著しく性能が低下します。
- 対策:可能であれば
JOINやWITH句を使った結合処理に書き換えることを検討してください。 - 確認:実行計画(Explain Plan)を取得し、インデックスが使われているか確認しましょう。
7. FAQ(よくある質問)
Q1. 副問い合わせと結合(JOIN)はどちらが速いですか?
A. ケースバイケースですが、現在はオプティマイザ(Oracle の最適化機能)が優秀なため、どちらで書いても内部的に同じ処理プランになることが多いです。ただし、相関副問い合わせは遅くなる傾向があるため、大量データの場合は JOIN や WITH 句への書き換えを推奨します。
Q2. UPDATE 文でも副問い合わせは使えますか?
A. はい、可能です。SET 句の値指定や、WHERE 句の条件指定に使用できます。
例:UPDATE employees SET salary = (SELECT …)
Q3. 副問い合わせのネスト(入れ子)に制限はありますか?
A. Oracle の仕様上、サブクエリのネストレベルに厳密な上限はありませんが、可読性とメモリ消費の観点から、深くても3階層程度に留めるのがベストプラクティスです。
8. まとめ
- 副問い合わせは、SQL の表現力を広げる必須テクニック。
- 1行なら
=、複数行ならINを使う。 - 相関副問い合わせは強力だがパフォーマンスに注意。
- 複雑になりそうなら
WITH句 で整理する。 NOT INにNULLが混ざると危険。NOT EXISTSを使う習慣をつける。
まずはサンプルコードをコピーして実行し、エラー(ORA-01427)をわざと出してみるなどして挙動を体感してみてください。
本記事は Oracle Database 19c を対象に解説します(他バージョンは画面や既定値が異なる場合があります)。
[参考]
Oracle Database SQL言語リファレンス 19c
💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?
Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?




コメント