結論:サブクエリ(副問い合わせ)とは「クエリの中に別のクエリを入れる」書き方で、まず1本目のSELECTで値やリストを出し、それを2本目のWHEREで使う、と分けて考えると迷いません。 「◯◯より高い」「◯◯に含まれる」のように、条件そのものをデータベースに計算させたいときに使います。
このサイトのSQLレッスンは、すべてブラウザの中でそのままSQLを実行できます(無料・登録不要・環境構築なし)。読みながら手を動かすのがいちばん早いです。
サブクエリ=「クエリの中のクエリ」
サブクエリは、SELECT文の中に括弧 ( ) でくくって埋め込む、もう1つのSELECT文のことです。日本語では「副問い合わせ」とも呼びます。
たとえば「平均より高い価格の商品を出したい」とき、値を直接書くことはできません。平均がいくつかは、データを見るまで分からないからです。そこでこう書きます。
SELECT name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);
内側の (SELECT AVG(price) FROM products) が先に動いて平均値を1つ返し、外側のWHEREがその値を使って絞り込みます。「内側が先、外側が後」 という順番さえ押さえれば、あとは組み立てるだけです。
この基本形は、サブクエリ ― クエリの中のクエリ のレッスンでブラウザにそのままSQLを打ち込んで確かめられます。数字を書き換えて何度でも実行できるので、「内側だけ先に実行するとどうなるか」を自分の目で見てみてください。
WHEREの中で値と比べる
サブクエリがいちばん活躍するのはWHEREの中です。前提として、WHEREでの絞り込みそのものに不安がある人は、先に WHERE ― 条件で絞り込む をブラウザで一周しておくと、サブクエリがすっと入ってきます。
サブクエリが返すものは、大きく2種類に分けて考えると整理できます。
- 1つの値(スカラ)を返す:
= ( ... )や> ( ... )のように比較演算子と組み合わせる。上の平均価格の例がこれです。 - 複数の行(リスト)を返す:
IN ( ... )と組み合わせる。次の節で扱います。
「返ってくるのは1個か、複数か」を意識するだけで、書くべき演算子が決まります。
IN サブクエリ ― リストに含まれるかで絞る
「東京の会員が出した注文だけ見たい」のように、別の表から条件のリストを作って、その中に含まれる行を選ぶ のが IN サブクエリです。
SELECT * FROM orders
WHERE member_id IN (SELECT id FROM members WHERE city = '東京');
内側が「東京の会員IDの一覧」を返し、外側が「その一覧に含まれる注文」を選びます。IN はもともとリストとの照合を行う演算子です。IN の基本(値をカンマ区切りで並べる書き方)に自信がなければ、DISTINCT・LIKE・IN・BETWEEN ― 絞り込みを広げる で先に手を動かしておくと、サブクエリ版がそのまま延長線上に見えます。
IN と EXISTS の使い分け
「別の表に一致する行があるか」を確かめる書き方には、IN のほかに EXISTS があります。考え方が少し違います。
INは 内側が作ったリストの中に、その値があるか を見る。EXISTSは 外側の1行ごとに内側を動かし、1件でも該当があるか(存在するか) だけを見る。中身の値は問わず、あるかないかだけを返します。
SELECT * FROM members m
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.member_id = m.id);
-- 「注文を1件でも出したことがある会員」だけが残る
EXISTS のように、外側の行の値を内側で使うサブクエリを 相関サブクエリ と呼びます。少し難しく感じるところですが、相関サブクエリと EXISTS ― 行ごとに問い合わせる で、実際に行が1つずつ処理される様子をブラウザで実行しながら追えます。ここが腹落ちすると、サブクエリの理解が一段深くなります。
サブクエリと JOIN、どっちを使う?
「別の表とつなぐ」という点で、サブクエリは JOIN ― 2つの表を結びつける と役割が重なります。ざっくりした使い分けはこうです。
- 両方の表の列を並べて表示したい → JOIN(会員名と注文金額を横に並べる、など)。
- 絞り込みの条件として別の表を使いたいだけ → サブクエリ(会員名は要らず、東京の会員の注文だけ残せればよい、など)。
どちらでも書ける場面も多いので、まずは「結果に相手の列が要るか?」で選ぶと迷いません。両方を実際に書いて見比べると違いが体でわかります。
つまずきやすいところ
初心者が最初にぶつかる代表的なエラーが、比較のつもりのサブクエリが複数の列を返してしまう ケースです。
-- ✗ 内側が2列返しているので、= と比べられない
WHERE member_id = (SELECT id, name FROM members WHERE ...);
= ( ... ) で比べるサブクエリは 1列・1行だけ を返さなければいけません。原因と直し方は サブクエリの列数が合わない(sub-select returns N columns) にまとめてあります。似た形で「集計とGROUP BYがかみ合っていない」エラーもよく出るので、必要なら SQLのエラー逆引き をブックマークしておくと安心です。
もう一つ、IN サブクエリの内側にNULLが混ざると、期待とずれた結果になることがあります。NULLの扱いは NULL ― 「値が無い」を正しく扱う で押さえておくと、原因不明のハマりを避けられます。
次に読む
- サブクエリ ― クエリの中のクエリ:まずここから。ブラウザで基本形を実行。
- 相関サブクエリと EXISTS ― 行ごとに問い合わせる:一歩進んだ「行ごとの問い合わせ」。
- CTE(WITH句)― クエリに名前を付けて組み立てる:深くネストしたサブクエリを読みやすく整理する書き方。
- JOIN ― 2つの表を結びつける:サブクエリとの使い分けを実際に手を動かして比較。
- SQLをゼロから順番にやりたい人は SQL(データ操作)コース をどうぞ。すべてブラウザで動かせて無料です。