Oracle SQL スカラー副問い合わせ:基礎から高速化の定石まで

Oracle Master Silver

SQLでデータを抽出する際、「別のテーブルにある値を1つだけ持ってきたい」「集計結果と比較してフィルタリングしたい」と悩むことはありませんか?

スカラー副問い合わせ(Scalar Subquery)は、SQL内で「1行1列」の値を返すサブクエリを使用し、定数や列のように扱う強力なテクニックです。

本記事では、Oracle Database 19c を前提に、スカラー副問い合わせの基本構文から、実務で役立つパフォーマンス(高速化)のポイントまで、初心者にも分かりやすく解説します。

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

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

結論・スカラー副問い合わせの要点リスト

時間がない方向けに、この機能の特性と注意点をまとめました。

  • 戻り値の鉄則: 必ず「1行かつ1列」の結果を返す必要がある(0行の場合は NULL になる)。
  • 配置場所: SELECT 句、WHERE 句、ORDER BY 句、CASE 式の中などで利用可能。
  • 動作イメージ: ループ処理のように、主問い合わせの1行ごとに副問い合わせが実行される(相関副問い合わせの場合)。
  • 注意点: 便利だが多用すると遅くなる可能性があるため、実行計画やキャッシュ効果の理解が必要。

1. スカラー副問い合わせとは?

スカラー副問い合わせとは、「単一の値(スカラー値)」を返す副問い合わせのことです。

通常、SELECT 文は表形式(複数行・複数列)の結果を返しますが、スカラー副問い合わせは、あたかも「数値の 100」や「文字列の ‘東京’」と同じように、SQLの一部として埋め込むことができます。

どのような場面で使うか

  • マスタ参照: 結合(JOIN)を書かずに、コード値に対応する名称を別のテーブルから取得する。
  • 集計比較: 「部門ごとの平均給与」など、集計値を条件に使用する。
  • ランク計算: 特定の条件を満たす件数を数えて表示する。

2. 基本構文と仕組み

最も一般的な構文は以下の通りです。カッコ ( ) で囲むのがルールです。

SELECT
    列A,
    (SELECT 対象列 FROM 別テーブル WHERE 条件) AS 別名
FROM
    主テーブル;

動作のフロー

  1. 主テーブルから1行読み込む。
  2. その行の値を使って、副問い合わせ(カッコの中)を実行する。
  3. 副問い合わせの結果(1つだけ)を、主問い合わせの結果列として表示する。
  4. 次の行へ進み、繰り返す。

3. 実践:スカラー副問い合わせの実行例

実際に動くSQLで確認してみましょう。ここでは従業員(Employees)と部門(Departments)のテーブルを使用します。

前提:テーブル作成とデータ準備

まずは環境を作成します。

-- 既存テーブルがある場合は削除(エラー無視)
-- DROP TABLE employees PURGE;
-- DROP TABLE departments PURGE;

-- 部門テーブル
CREATE TABLE departments (
    dept_id   NUMBER PRIMARY KEY,
    dept_name VARCHAR2(50)
);

-- 従業員テーブル
CREATE TABLE employees (
    emp_id    NUMBER PRIMARY KEY,
    emp_name  VARCHAR2(50),
    dept_id   NUMBER,
    salary    NUMBER
);

-- データ登録
INSERT INTO departments VALUES (1, 'Sales');
INSERT INTO departments VALUES (2, 'Dev');

INSERT INTO employees VALUES (1, 'Tanaka Taro', 1, 300000);
INSERT INTO employees VALUES (2, 'Yamada Hanako', 1, 350000);
INSERT INTO employees VALUES (3, 'Sato Jiro', 2, 400000);
INSERT INTO employees VALUES (4, 'Suzuki Ichiro', 2, 450000);
COMMIT;
SQL> SELECT * FROM departments;

DEPT_ID DEPT_NAME
---------- --------------------
1 Sales
2 Dev

SQL> SELECT * FROM employees;

EMP_ID EMP_NAME DEPT_ID SALARY
---------- -------------------- ---------- ----------
1 Tanaka Taro 1 300000
2 Yamada Hanako 1 350000
3 Sato Jiro 2 400000
4 Suzuki Ichiro 2 450000

ケース1:他テーブルの値を「列」として取得する(SELECT句)

JOIN を使用せずに、従業員一覧に「部門名」を表示します。

SELECT 
    e.emp_name,
    e.salary,
    -- ここがスカラー副問い合わせ
    (SELECT d.dept_name 
     FROM   departments d 
     WHERE  d.dept_id = e.dept_id) AS dept_name
FROM 
    employees e;

実行結果:

SQL> SELECT
2 e.emp_name,
3 e.salary,
4 (SELECT d.dept_name
5 FROM departments d
6 WHERE d.dept_id = e.dept_id) AS dept_name
7 FROM
8 employees e;

EMP_NAME SALARY DEPT_NAME
-------------------- ---------- --------------------
Tanaka Taro 300000 Sales
Yamada Hanako 350000 Sales
Sato Jiro 400000 Dev
Suzuki Ichiro 450000 Dev

解説:

e.dept_id(主問い合わせの値)を副問い合わせ内で使用しています(これを「相関」と呼びます)。部門テーブルに該当するIDがない場合、結果は NULL になります。これは LEFT OUTER JOIN と似た挙動です。

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

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

ケース2:集計値と比較してフィルタリングする(WHERE句)

「所属部門の平均給与」よりも高い給与をもらっている社員を探します。

SELECT 
    e1.emp_name,
    e1.salary
FROM 
    employees e1
WHERE 
    e1.salary > (
        -- 部門ごとの平均給与を計算して返す
        SELECT AVG(e2.salary) 
        FROM   employees e2 
        WHERE  e2.dept_id = e1.dept_id
    );

実行結果:

SQL> SELECT
2 e1.emp_name,
3 e1.salary
4 FROM
5 employees e1
6 WHERE
7 e1.salary > (
8 SELECT AVG(e2.salary)
9 FROM employees e2
10 WHERE e2.dept_id = e1.dept_id
11 );

EMP_NAME SALARY
-------------------- ----------
Yamada Hanako 350000
Suzuki Ichiro 450000

解説:

各社員の行ごとに、「その社員が所属する部門の平均」を計算し、比較を行っています。

(Sales部の平均: 325,000 / Dev部の平均: 425,000)


4. トラブルシューティング(よくあるエラー)

スカラー副問い合わせを使用する際、頻出するエラーとその対処法です。

エラーコードエラーメッセージ原因と対処
ORA-01427単一行副問合せにより2つ以上の行が戻されます原因: 副問い合わせの結果が2件以上ヒットしています。
対処: 結合条件を見直すか、ROWNUM = 1MAX() 等で1件に絞ってください。
ORA-00936式がありません原因: カッコ内の SELECT 句に列が指定されていない、またはカンマの誤記など。
対処: 構文を確認してください。
(結果不正)意図せず NULL が返る原因: 副問い合わせの条件に合致するデータが副側に存在しない(0件)。
対処: NVL( (副問合せ), 0 ) のように、NULL時のデフォルト値を設定します。

5. 運用・監視・パフォーマンス上の注意点

スカラー副問い合わせは便利ですが、プロの現場では「パフォーマンス」に細心の注意を払う必要があります。

メリット

  • SQLが直感的に読める(結合の記述が複雑にならない)。
  • GROUP BY を主問い合わせで行わずに、個別の集計値を付与できる。

デメリット・落とし穴

  • N+1問題に近い挙動: 行数分だけ副問い合わせが実行される可能性があるため、主テーブルが数百万行あると激しく遅延します。
  • コンテキストスイッチ: SQLエンジン内でコンテキストの切り替えが発生し、オーバーヘッドになる場合があります。

高速化のヒント(Oracleの機能)

Oracle Databaseには「スカラー副問い合わせのキャッシュ機能」があります。

入力値(相関条件の値)が同じであれば、過去の実行結果をメモリ上のハッシュテーブルから再利用します。

  • 効果的: 部門IDの種類が少ない(例: 10種類)が、従業員は多い(例: 1万人)場合。キャッシュヒット率が高まり高速です。
  • 非効果的: 相関条件の値がほぼユニーク(全件バラバラ)な場合。キャッシュが効かず、全件都度実行となり非常に遅くなります。この場合は LEFT JOIN への書き換えを検討してください。

6. FAQ:よくある質問

Q1. 副問い合わせの結果が0件だった場合、エラーになりますか?

A. いいえ、エラーにはなりません。

スカラー副問い合わせの結果が0行の場合、Oracleは NULL を返します。NVL 関数などで NULL を 0 や ‘-‘ に変換することをお勧めします。

Q2. スカラー副問い合わせと通常の結合(JOIN)、どちらが良いですか?

A. 取得する列数とデータ量によります。

  • 1列だけ参照したい、かつデータの種類(カーディナリティ)が少ない場合:スカラー副問い合わせが手軽で高速な場合があります。
  • 複数列参照したい、またはデータ量が膨大な場合:通常の LEFT JOIN の方が効率的です。

Q3. SELECT句以外でも使えますか?

A. はい。

SET 句(UPDATE文)、VALUES 句(INSERT文)、ORDER BY 句などで使用可能です。特に ORDER BY (副問い合わせ) は、マスタテーブルの「表示順カラム」でソートしたい場合に便利です。


7. まとめ

スカラー副問い合わせは、SQLの表現力を広げる重要なテクニックです。

  1. 1行1列を返すのが絶対条件(複数行返ると ORA-01427)。
  2. SELECT句やWHERE句で、動的な値取得や条件設定に使える。
  3. キャッシュ機能が効く場合は高速だが、ユニークな値が多い場合はJOINを検討する。
  4. 結果がない場合は NULL になる点に注意して実装する。

まずは小さなデータセットで挙動を確認し、実行計画を見ながら適切な場面で活用してみてください。


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



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

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

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

コメント

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