スカラー副問い合わせ(値を返す SELECT)の使い方
shain社員
| name | salary |
|---|---|
| 佐藤 | 720000 |
| 加藤 | 610000 |
| 中村 | 530000✕ |
| 井上 | 505000✕ |
| … | |
salary > (SELECT avg(salary) FROM shain)平均より上の行
| name | salary |
|---|---|
| 佐藤 | 720000 |
| 加藤 | 610000 |
| … | |
WHERE salary > (SELECT avg(salary) FROM shain)
- 取り消し線= 条件に合わないので結果に出てこない行
- 社員 18 行 → 平均より上の行 5 行(13 行が消えます)
18 行のうち残るのは 5 行です。副問い合わせは先に 1 回だけ計算され、その値と各行を比べています。
1 行 1 列だけを返す SELECT は、そのまま「値」として使えます。 これをスカラー副問い合わせと呼びます。WHERE に集約関数を直接書けない問題は、これで解決します。ただし返る行数が 1 でなくなった瞬間に、エラーになるか、黙って結果が消えます。
主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2
このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。
011 行 1 列なら「値」として扱える
結論: 括弧で囲んだ SELECT が 1 行 1 列だけを返すなら、それは値と同じように書ける場所すべてに置けます。
置ける場所は広く、WHERE の右辺・SELECT の列・ORDER BY のキーなど、式が書けるところならどこでも構いません。
条件は 2 つだけです。
- 列が 1 つであること
- 行が 1 行であること(0 行と複数行の扱いは後述)
avg() や max() のような集約関数は、必ず 1 行 1 列を返すので相性が良く、実際いちばん多い使い道です。
SELECT name, salaryどの列を出すかFROM shainどの表を見るかWHERE salary >各行の給与と…(SELECT avg(salary) FROM shain)…全体の平均(1 つの値)を比べるORDER BY salary DESC並び順
内側の SELECT は外側の行に関係なく 1 回だけ計算されます。外側の行ごとに計算が変わる書き方は「相関副問い合わせ」と呼び、別のページで扱います。
SELECT name, salary
FROM shain
WHERE salary > (SELECT avg(salary) FROM shain)
ORDER BY salary DESC;| name | salary |
|---|---|
| 鈴木 | 980000 |
| 佐藤 | 720000 |
| 山田 | 700000 |
| 清水 | 650000 |
| 加藤 | 610000 |
(5 行)
5 人です。平均は 542002.77… なので、61 万の加藤までが残ります。
SELECT name, salary
FROM shain
WHERE salary = (SELECT max(salary) FROM shain);| name | salary |
|---|---|
| 鈴木 | 980000 |
(1 行)
「いちばん高い人」は ORDER BY と LIMIT でも出せますが、この書き方なら同額が複数いれば全員返ります。ここでは 1 人です。
02⚠️ よくある間違い:WHERE に集約関数は直接書けない
結論: WHERE salary > avg(salary) はエラーになります。 これがスカラー副問い合わせを使ういちばんの理由です。
理由は評価の順番です。WHERE は1 行ずつ絞り込む段階で働きますが、avg() は全部の行を見終わってからでないと計算できません。まだ存在しない値とは比べられないので、SQL 側で禁止されています。
副問い合わせにすると、内側が先に計算されて 1 つの値になり、外側の WHERE はその値と比べるだけになります。順番の問題が消えるわけです。
⚠️ 「集約したあとに絞る」なら HAVING を使う、という別の道もあります。HAVING はまとまりごとの絞り込みなので、全体の平均と各行を比べたい今回の用途には使えません。
SELECT name, salary
FROM shain
WHERE salary > avg(salary);「WHERE に集約関数は書けない」と言われます。書き方の問題ではなく、評価の順番の問題です。
03SELECT 句にも書ける
結論: SELECT の列としてスカラー副問い合わせを置くと、全行に同じ値が並びます。
各行の値と全体の値を横に並べて見比べたいときに使います。「自分の給与」と「全体の平均」が同じ行に出るので、差を計算したり、そのまま表として読ませたりできます。
⚠️ 同じ値が 18 行ぶん並ぶので、大きな表では無駄に見えます。実際には多くの製品が「内側は 1 回だけ計算する」と判断してくれますが、行ごとに計算し直す形(相関副問い合わせ)と見た目が似ているため、読む人が誤解しやすい点には注意してください。
SELECT name,
salary,
(SELECT round(avg(salary)) FROM shain) AS zentai_heikin
FROM shain
ORDER BY salary DESC;| name | salary | zentai_heikin |
|---|---|---|
| 鈴木 | 980000 | 542003 |
| 佐藤 | 720000 | 542003 |
| 山田 | 700000 | 542003 |
| 清水 | 650000 | 542003 |
| 加藤 | 610000 | 542003 |
| 伊藤 | 530000 | 542003 |
(18 行 … うち先頭 6 行を表示)
18 行すべてに同じ 542003 が入ります。1 つの値を全行に配っている、という形がそのまま見えます。
⚠️ この書き方は SQL Server / IBM Db2 では使えません(round(x)(引数 1 つ))。製品ごとの対応表
04⚠️ よくある間違い:2 行以上返るとエラーになる
結論: スカラー副問い合わせが 2 行以上返すと、実行時にエラーで止まります。
「値」として使うつもりの場所に値が 2 つ来てしまうので、比べようがありません。
これは開発時には親切なエラーです。困るのは、テストデータでは 1 行しか返らなかったが、本番のデータでは 2 行返るという場合です。書いた時点では気づけません。
対処は 2 つ。
- 本当に 1 行に決まる条件か確かめる(一意な列で絞っているか)
- 複数返りうるなら
INやEXISTSを使う。これらは集合を相手にする述語なので、何行返っても構いません
「1 つに決まるはず」という思い込みが崩れる場所なので、副問い合わせの条件が一意かどうかは毎回確かめてください。
SELECT name
FROM shain
WHERE busho_id = (SELECT busho_id
FROM busho
WHERE parent_busho_id = 1);「副問い合わせが 2 行以上返した」と言われます。経営の直下には部署が 4 つあるためです。
SELECT name
FROM shain
WHERE busho_id = (SELECT busho_id
FROM busho
WHERE busho_name = '開発')
ORDER BY shain_id;| name |
|---|
| 佐藤 |
| 田中 |
| 中村 |
| 小林 |
(4 行)
busho_name は一意なので必ず 1 行です。4 人が返ります。
05⚠️ よくある間違い:0 行だと黙って結果が消える
結論: 副問い合わせが 1 行も返さないと、その値は NULL になります。 エラーにはなりません。
そして NULL との比較は unknown になるので、外側の WHERE を通る行が 1 つも無くなります。結果は 0 行です。
2 行返るときはエラーで止まるのに、0 行のときは静かに「該当なし」が返る——ここが非対称で危ないところです。「条件に合うデータが無いんだな」と読んでしまい、副問い合わせ側の条件を間違えていることに気づけません。
確かめ方は簡単です。 副問い合わせだけを単体で実行してみてください。何行返るかが一目で分かります。
SELECT (SELECT busho_id
FROM busho
WHERE busho_name = '存在しない部署') AS kekka;| kekka |
|---|
| NULL |
(1 行)
エラーではなく NULL が返ります。これが外側の比較に渡ると unknown になります。
SELECT name
FROM shain
WHERE busho_id = (SELECT busho_id
FROM busho
WHERE busho_name = '存在しない部署');| name |
|---|
(0 行)
社員は 18 人いますが 1 人も返りません。「該当者なし」に見えますが、実際は部署名の書き間違いです。
自分で打ってみる
このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。
押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。
解いてみる
読んだだけでは書けるようになりません。実行結果で採点します。
ここで出てくる関数を引く
関連するトピック
- IN / EXISTS複数行が返りうるときは、こちらの述語を使います。
- 相関副問い合わせ外側の行ごとに計算し直す書き方です。
- HAVING 句まとまりごとに絞るときはこちらです。
- 集約関数副問い合わせの中でいちばんよく使う関数です。
根拠(一次情報)
- PostgreSQL 18 マニュアル: 副問い合わせ(スカラー副問い合わせ)一次情報・確認 2026-08-03
- PostgreSQL 18 マニュアル: 集約関数を使った問い合わせ一次情報・確認 2026-08-03
- Microsoft SQL Server: 副問い合わせの基礎一次情報・確認 2026-08-03