はじめに
サブクエリ(副問い合わせ)は「クエリの中に別のクエリを埋め込む」構文です。CASE式と並んでSQLの表現力を大きく広げる要素ですが、種類が多く混同しやすいため、種類ごとに整理します。
CASE式について
この後の流れ
- サブクエリの分類
- スカラサブクエリ
- IN/NOT INサブクエリ
- 相関サブクエリ
- EXISTS/NOT EXISTS
- FROM句サブクエリ(インラインビュー)とCTE
- まとめ
1. サブクエリの分類
| 種類 | 書く場所 | 返す値 |
|---|---|---|
| スカラサブクエリ | SELECT句、WHERE句 | 単一の値(1行1列) |
| IN句サブクエリ | WHERE句 | 複数行の1列(リスト) |
| 相関サブクエリ | WHERE句、SELECT句 | 外側の行ごとに再評価される値 |
| EXISTS句 | WHERE句 | 真偽値(存在確認のみ) |
| FROM句サブクエリ | FROM句 | テーブルのような結果セット |
2. スカラサブクエリ
必ず1行1列を返すサブクエリです。SELECT句に直接書けます。
SELECT
product_name,
price,
(SELECT AVG(price) FROM products) AS avg_price,
price - (SELECT AVG(price) FROM products) AS diff_from_avg
FROM products;
全体平均との差分を、各行に対して1回のクエリで算出できます。
3. IN/NOT INサブクエリ
-- 東京支店に所属する社員が担当した注文のみ抽出
SELECT * FROM orders
WHERE employee_id IN (
SELECT employee_id FROM employees WHERE branch = '東京'
);
注意: NOT INはサブクエリの結果にNULLが1件でも含まれると、想定外に0件になります。NOT INを使う際はサブクエリ側でWHERE column IS NOT NULLを必ず付けるか、後述のNOT EXISTSを使う方が安全です。
4. 相関サブクエリ
外側のクエリの各行を参照しながら、行ごとに再評価されるサブクエリです。
-- 各カテゴリの平均価格より高い商品を抽出
SELECT p1.product_name, p1.category_id, p1.price
FROM products p1
WHERE p1.price > (
SELECT AVG(p2.price)
FROM products p2
WHERE p2.category_id = p1.category_id -- 外側のカテゴリを参照
);
IN句サブクエリとの違いは、外側の行に依存して結果が変わる点です。そのため一般的に実行コストが高く、大規模データでは注意が必要です。
5. EXISTS/NOT EXISTS
「値」ではなく「存在するかどうか」だけを見る場合はEXISTSが適しています。NULLの罠もなく、可読性・性能面で有利なケースが多いです。
-- 一度でも注文したことがある顧客
SELECT * FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);
-- 一度も注文したことがない顧客
SELECT * FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);
| 比較項目 | IN | EXISTS |
|---|---|---|
| NULLの扱い | NOT INで罠がある | 影響を受けない |
| 用途 | 値のリストと比較したい | 存在有無だけ確認したい |
| 可読性 | シンプルな条件で読みやすい | 相関条件がある場合に明確 |
6. FROM句サブクエリ(インラインビュー)とCTE
FROM句にサブクエリを書くと、その結果を一時的なテーブルとして扱えます。
-- FROM句サブクエリ
SELECT category_id, max_price
FROM (
SELECT category_id, MAX(price) AS max_price
FROM products
GROUP BY category_id
) AS category_max
WHERE max_price >= 5000;
同じことはWITH句(CTE: 共通テーブル式)でも書けます。可読性・再利用性の観点でCTEの方が推奨されることが多いです。
WITH category_max AS (
SELECT category_id, MAX(price) AS max_price
FROM products
GROUP BY category_id
)
SELECT * FROM category_max WHERE max_price >= 5000;
7. まとめ
| シーン | 推奨構文 |
|---|---|
| 単一値との比較 | スカラサブクエリ |
| 固定リストとの比較 | IN(NULL混入がない前提) |
| 行ごとの相関計算 | 相関サブクエリ |
| 存在有無の判定 | EXISTS / NOT EXISTS |
| 複雑な中間結果の再利用 | CTE(WITH句) |
さいごに
サブクエリは「何を返すか(値・リスト・真偽値・テーブル)」で使い分けを覚えると迷いにくくなります。次回はウィンドウ関数について解説します。