WHERE句のサブクエリ
このレッスンで分かること
- 単一値返却なら
=<>、複数値返却はINANYALLEXISTSを使い分けますNOT INは NULL に弱い、NOT EXISTSで書き換えるのが安全- 最小例は
WHERE id IN (SELECT customer_id FROM orders);
WHERE句のサブクエリ とは
WHERE句の中でサブクエリを使う方法を、実践的な例を通して学びます。本レッスンでは、WHERE句のサブクエリ の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
なぜ重要か
「他のテーブルに該当がある行」「集計結果と比較した行」「最大値の行」を絞り込むのは、SQL を書く上で日常的に発生します。WHERE 句にサブクエリを書けば、こうした条件をスマートに表現できます。本レッスンでは 単一値で比較する場合(=, <, >)と、複数値で比較する場合(IN, ANY, ALL, EXISTS)の使い分けを学びます。とくに『複数行を返すサブクエリに = を使うと壊れる』というハマりポイントは、現場でもよく見るミスなので押さえておきましょう。
構文・キーワード解説
WHERE 句のサブクエリには次の比較演算子が使えます。
SQL クエリ
-- 単一値を返すサブクエリ
WHERE column = (SELECT ...)
WHERE column > (SELECT AVG(price) FROM ...)
-- 複数値を返すサブクエリ
WHERE column IN (SELECT ...)
WHERE column NOT IN (SELECT ...)
WHERE column > ANY (SELECT ...) -- 一つでも該当があれば真
WHERE column > ALL (SELECT ...) -- 全てに該当する場合のみ真
WHERE EXISTS (SELECT 1 FROM ... WHERE ...)ステップごとの動作
非相関サブクエリは「内側を 1 回実行 → 結果を外側の比較値として使う」というシンプルな流れです。
単一値と複数値の違い
サブクエリが返す行数で書き方が変わります。
| 返ってくる行 | 使える演算子 | 失敗するパターン |
|---|---|---|
| 1 行 1 列 | =, <, >, <=, >=, <> | 0 行のときは NULL になり常に false |
| 複数行 1 列 | IN, ANY, ALL, EXISTS | = を使うと実行時エラー |
| 1 行 N 列(行コンストラクタ) | (a, b) = (SELECT a, b ...) | 列数不一致でエラー |
代表例
IN で「注文がある顧客」を取り出す
SQL クエリ
SELECT id, name
FROM customers
WHERE id IN (SELECT DISTINCT customer_id FROM orders);orders テーブルに登場する customer_id をサブクエリで集め、その集合に含まれる顧客だけを返します。
> ALL で全カテゴリの最高値より高い商品
SQL クエリ
SELECT name, price
FROM products
WHERE price > ALL (
SELECT MAX(price)
FROM products
WHERE category <> '家電'
GROUP BY category
);家電以外の各カテゴリの最高価格すべてを上回る価格、という条件です。ANY だと「いずれか 1 つ以上」になる点と対比して覚えると整理しやすくなります。
EXISTS で「注文を持っている顧客」を取り出す
SQL クエリ
SELECT id, name
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);EXISTS は「サブクエリが 1 行でも返したら真」という意味で、JOIN しなくても存在チェックができます。重複が出ないのもメリットです。
NOT IN の落とし穴
注意 サブクエリの結果に NULL が混ざると、
NOT INは すべての行が false になります。customer_idに NULL を許容している場合はNOT EXISTSで書き換えるのが安全です。
SQL クエリ
-- NULL が含まれていても安全
SELECT id, name
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);IN と EXISTS の使い分け
結果が同じになることが多い IN と EXISTS ですが、内部の動作と得意分野は微妙に違います。
IN: 内側で値の集合を作ってから、外側と突き合わせる。集合が小さければ高速EXISTS: 外側の各行に対し、内側を 1 件でも見つけたら打ち切る。短絡評価が効く
SQL クエリ
-- IN 形式
SELECT id, name FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE amount >= 5000);
-- EXISTS 形式
SELECT id, name FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.id AND o.amount >= 5000
);| 観点 | IN | EXISTS |
|---|---|---|
| 内部動作 | 集合を作って突き合わせ | 行ごとに 1 件見つけたら打ち切り |
| NULL に強い | 弱い (NOT IN で詰む) | 強い |
| 相関サブクエリ | 不要 | 必要 |
| 読みやすさ | 集合の所属を表現 | 「存在するか」を直球で表現 |
「サブクエリが返す行が少ない」「重複を出したくない」「外側と相関させたい」場合は EXISTS が便利。単純に集合に含まれるかだけ確認したいなら IN のほうが読みやすい。
IN と EXISTS は等価でないことがある。NULL を含む場合と相関の有無で挙動が変わるため、相手側の前提を確認してから選ぶ。
ここまでの要点
単一値は = 系、複数値は IN / ANY / ALL / EXISTS。NULL を含むなら NOT EXISTS が安全。IN と EXISTS は等価でない場面があるので前提を確認。
まとめ
- 単一値を返すサブクエリには
=<>などが使える - 複数値を返すサブクエリには
INANYALLEXISTSを使う EXISTSは重複を生まず、NULL 混在にも強いNOT INは NULL に弱いので、可能な限りNOT EXISTSを選ぶ
次のレッスン
次は FROM句のサブクエリ です。FROM句でサブクエリを使う方法を学習し、複雑なデータ抽出に挑戦します。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- WHERE サブクエリ の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. WHERE サブクエリ とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
テーブル構造
schema.sql
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(40)
);
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
amount INT
);
INSERT INTO customers VALUES
(1, '佐藤'),
(2, '鈴木'),
(3, '田中'),
(4, '高橋'),
(5, '伊藤');
INSERT INTO orders VALUES
(101, 1, 3000),
(102, 1, 1500),
(103, 2, 8000),
(104, 4, 5000);期待される出力
| id | name |
|---|---|
| 1 | 佐藤 |
| 2 | 鈴木 |
| 4 | 高橋 |