【Oracle】スカラー副問合せ(サブクエリ)徹底解説

【Oracle】スカラー副問合せ(サブクエリ)徹底解説

記事の文字数:3909

Oracleにおける「スカラー副問合せ」の定義、構文、代表的な使い方を解説します。またパフォーマンス上の注意点、よくあるエラーとその対処、実務でのベストプラクティスを初心者〜中級者向けにわかりやすくまとめた実践ガイドです。

Oracle SQLを学ぶ中で「スカラー副問合せ」という言葉を目にしたことはありませんか? スカラー副問合せとは、SELECT文の中でさらにSELECT文を記述し、単一の値を返す副問合せのことを指します。集計処理や動的な値の取得に非常に便利ですが、使い方を誤ると ORA-01427 エラーの原因にもなり、初心者がつまずきやすいポイントです。

本記事では、スカラー副問合せの基本的な仕組みから、実際の使用例、エラーが起こるケースとその回避方法までを徹底解説します。Oracle SQLを効率的に扱えるようになりたい方は、ぜひ参考にしてください。

スカラー副問合せとは?

スカラー副問合せ(スカラーサブクエリ)は、単一の値(スカラー値)を返す副問合せ(サブクエリ)のことです。主にSELECT句やWHERE句など式として使われ、1行1列の結果を期待します。Oracleでは、スカラー副問合せは(SELECT ...) の形式で記述します。

スカラー副問合せの特徴

  • 0行(NULL)または1行1列の値を返すことが期待される。
  • 2行以上を返すと ORA-01427: single-row subquery returns more than one row というエラーになる。
  • 相関(外側の行に依存する)する場合は行ごとに評価される。
    • 非相関副問合せ は外側の行に依存しないため、DBは最適化により一度だけ実行する場合があります。
    • 相関副問合せ は外側の行の値を参照するため、基本的に外側の行ごとに評価されます(結果、コストが高くなる可能性あり)。

スカラー副問合せでよく使われるパターンとSQL例

動作確認の前提

本記事での解説は以下データを登録していることを前提とします。

事前登録データ(DDL)
-- departments テーブル
-- DROP TABLE departments CASCADE CONSTRAINTS;
CREATE TABLE departments (
dept_id NUMBER PRIMARY KEY,
dept_name VARCHAR2(50)
);
INSERT INTO departments (dept_id, dept_name) VALUES (10, 'Sales');
INSERT INTO departments (dept_id, dept_name) VALUES (20, 'HR');
INSERT INTO departments (dept_id, dept_name) VALUES (30, 'IT');
INSERT INTO departments (dept_id, dept_name) VALUES (40, 'Finance');
-- employees テーブル
-- DROP TABLE employees CASCADE CONSTRAINTS;
CREATE TABLE employees (
emp_id NUMBER PRIMARY KEY,
emp_name VARCHAR2(50),
dept_id NUMBER,
job VARCHAR2(30),
salary NUMBER,
CONSTRAINT fk_dept FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
-- データ挿入
-- Sales 部署(平均よりやや低い)
INSERT INTO employees VALUES (1001, 'Alice', 10, 'Sales Rep', 4000);
INSERT INTO employees VALUES (1002, 'Bob', 10, 'Sales Rep', 4200);
-- HR 部署(中間層)
INSERT INTO employees VALUES (1003, 'Carol', 20, 'HR Manager', 5000);
-- IT 部署(平均より高い)
INSERT INTO employees VALUES (1004, 'Dave', 30, 'Developer', 7000);
INSERT INTO employees VALUES (1005, 'Eve', 30, 'Developer', 7200);
-- Finance 部署(さらに高い)
INSERT INTO employees VALUES (1006, 'Frank', 40, 'Accountant', 9000);
-- bonuses テーブル
-- DROP TABLE bonuses CASCADE CONSTRAINTS;
CREATE TABLE bonuses (
job VARCHAR2(30) PRIMARY KEY,
bonus_pct NUMBER
);
-- jobごとにボーナス率を定義(単純なパーセント加算額)
INSERT INTO bonuses VALUES ('Sales Rep', 500); -- Sales Rep に+500
INSERT INTO bonuses VALUES ('HR Manager', 1000); -- HR Manager に+1000
INSERT INTO bonuses VALUES ('Developer', 1500); -- Developer に+1500
INSERT INTO bonuses VALUES ('Accountant', 800); -- Accountant に+800
-- summary_table 作成
-- DROP TABLE summary_table CASCADE CONSTRAINTS;
CREATE TABLE summary_table (
dept_id NUMBER,
emp_count NUMBER
);
COMMIT;
テーブルデータ

departments テーブル

DEPT_IDDEPT_NAME
10Sales
20HR
30IT
40Finance

employees テーブル

EMP_IDEMP_NAMEDEPT_IDJOBSALARY
1001Alice10Sales Rep4000
1002Bob10Sales Rep4200
1003Carol20HR Manager5000
1004Dave30Developer7000
1005Eve30Developer7200
1006Frank40Accountant9000

bonuses テーブル

JOBBONUS_PCT
Sales Rep500
HR Manager1000
Developer1500
Accountant800

summary_table テーブル

SELECT句でスカラー副問合せを利用

-- SELECT句での例(スカラー副問合せ)
SELECT e.emp_id,
e.emp_name,
(SELECT d.dept_name
FROM departments d
WHERE d.dept_id = e.dept_id) AS department
FROM employees e;

実行結果

EMP_IDEMP_NAMEDEPARTMENT
1001AliceSales
1002BobSales
1003CarolHR
1004DaveIT
1005EveIT
1006FrankFinance

この例では、(SELECT d.dept_name ...) が各行ごとに実行され、対応する部門名が取得されます。副問合せが該当行を返さない場合はNULLになります。

WHERE句でスカラー副問合せを利用(比較条件)

-- 例: 社員の給与が全社平均より高い社員を取得
SELECT *
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

実行結果

EMP_IDEMP_NAMEDEPT_IDJOBSALARY
1004Dave30Developer7000
1005Eve30Developer7200
1006Frank40Accountant9000

このケースの副問合せは非相関なので一度だけ評価されます。

UPDATEでスカラー副問合せを利用(更新値に副問合せを使う)

-- 例: ボーナス率テーブルからjobごとに加算する
UPDATE employees e
SET salary = salary + (
SELECT b.bonus_pct
FROM bonuses b
WHERE b.job = e.job
)
WHERE EXISTS (
SELECT 1 FROM bonuses b WHERE b.job = e.job
);

UPDATE前(SELECT * FROM employees;

EMP_IDEMP_NAMEDEPT_IDJOBSALARY
1001Alice10Sales Rep4000
1002Bob10Sales Rep4200
1003Carol20HR Manager5000
1004Dave30Developer7000
1005Eve30Developer7200
1006Frank40Accountant9000

UPDATE後(SELECT * FROM employees;

EMP_IDEMP_NAMEDEPT_IDJOBSALARY
1001Alice10Sales Rep4500
1002Bob10Sales Rep4700
1003Carol20HR Manager6000
1004Dave30Developer8500
1005Eve30Developer8700
1006Frank40Accountant9800

注意: ( SELECT b.bonus_pct FROM bonuses b WHERE b.job = e.job ) が複数行を返さないことを保証する必要があります。

INSERTでスカラー副問合せを利用(挿入時に計算)

INSERT INTO summary_table (dept_id, emp_count)
SELECT d.dept_id,
(SELECT COUNT(*) FROM employees e WHERE e.dept_id = d.dept_id)
FROM departments d;

INSERT後( SELECT * FROM summary_table;

DEPT_IDEMP_COUNT
102
201
302
401

相関副問合せ・非相関副問合せの違いまとめ

  • 相関副問合せ(correlated): 外側のクエリのカラムを参照する副問合せ。例:WHERE b.job = e.job のように e を参照する。外側の行ごとに評価される。パフォーマンスに注意。
  • 非相関副問合せ(uncorrelated): 外側クエリに依存しないためDBが一度実行してキャッシュすることが多い。

実務では、外側の行ごとに実行される相関副問合せは JOIN や WITH (共通テーブル式) に書き換えられることが多いです。

よく遭遇するエラーと対処法(例:ORA-01427)

以下のSQLは、各部署ごとに「その部署の従業員の給与」をスカラー副問合せで取得しようとしています。 しかし、同じ部署に複数の従業員がいるため、副問合せが複数行を返しエラーとなります。

SELECT d.dept_id,
(SELECT e.salary
FROM employees e
WHERE e.dept_id = d.dept_id) AS one_salary
FROM departments d;

エラー例

ORA-01427: 単一行副問合せにより2つ以上の行が戻されます

原因: スカラー副問合せが2行以上を返している。

対処法

解決策の例としては、副問合せに MAX や MIN を使って1行に絞ることです。

SELECT d.dept_id,
(SELECT MAX(e.salary)
FROM employees e
WHERE e.dept_id = d.dept_id) AS max_salary
FROM departments d;

実行結果

DEPT_IDMAX_SALARY
409800
308700
104700
206000

スカラー副問合せによるパフォーマンス上の注意点と改善手法

スカラー副問合せは便利だが、使い方次第でパフォーマンスを悪化させる。特に相関副問合せは外側の各行で実行されるため、次の点を確認する。

改善のためのポイント

  • 非相関化できないか検討: 同じ値を多回計算しているなら一度だけ計算するようにWITH句(共通テーブル式)やマテリアライズを使う。

    WITH avg_sal AS (
    SELECT AVG(salary) AS avg_salary FROM employees
    )
    SELECT e.*, (SELECT avg_salary FROM avg_sal) FROM employees e;
  • JOINに書き換える: 部門名などを参照する場合はJOIN(LEFT JOIN/INNER JOIN)に置換するとオプティマイザがより効率的な計画を作ることが多い。

    SELECT e.emp_id,
    e.emp_name,
    d.dept_name
    FROM employees e
    LEFT JOIN departments d ON e.dept_id = d.dept_id;
  • 集約関数を使って1行にする: 副問合せの結果を集約して1行にする。

  • インデックスを活用: 副問合せ内で使われる列(join条件や検索条件)に適切なインデックスがあるか確認。

  • 実行計画(EXPLAIN PLAN)を確認: EXPLAIN PLAN FOR ...DBMS_XPLAN.DISPLAY で実行計画をチェックして、繰り返し実行されているかを確認する。

  • バインド変数を使う: 繰り返し実行されるクエリではバインド変数を利用してライブラリキャッシュの有効活用を促す。

Oracle固有の注意点

  • Oracleのオプティマイザは非相関副問合せを一度だけ評価する最適化を行うが、相関副問合せは基本的に行ごとに評価されるため注意が必要。
  • 12c以降の機能(FETCH FIRST など)を活用すると副問合せ内で安全に1行だけ取り出せる。

スカラー副問合せまとめ

スカラー副問合せは、SQLを簡潔に書ける便利な表現ですが、使い方を誤ると ORA-01427 のようなエラーやパフォーマンス問題を招きます。以下を意識して使いましょう、

  • 副問合せが必ず1行に収まることを確認しているか?(集約・制限を検討)
  • 相関副問合せで行ごとに評価される影響を考慮したか?(行数×副問合せコスト)
  • JOIN、WITH、または分析関数で書き換えられないか検討したか?
  • 副問合せ内の検索列に適切なインデックスが張られているか?
  • 実行計画を EXPLAIN PLAN / DBMS_XPLAN で確認したか?
  • 副問合せが0行を返す時の取り扱い(NULL)を設計しているか?(NVL, COALESCE など)

以上で本記事の解説を終わります。
よいITライフを!
Scroll to Top