コマンド道場

FROM 句の副問い合わせ(派生表)の使い方

作った表を、次の問い合わせの入力にする

shain元の表

nametaishoku_on
山本2024-03-31
高橋2025-09-30
鈴木NULL

t在籍中だけの表

nametaishoku_on
鈴木NULL
佐藤NULL
田中NULL

結果部署ごとの人数

busho_nameninzu
開発4
営業3
マーケ2
  • 元の表 18在籍中だけの表 16部署ごとの人数 6
  • 前の段の結果が次の段の入力になります。表が 1 つ増えたつもりで読んでください

真ん中の t が派生表です。左に写っている退職日入りの 2 人が消えて 16 行になり、それを表として数えて 6 行になりました。

FROM の中に副問い合わせを書くと、その結果を 1 つの表として扱えます。 これを派生表と呼びます。1 つの SELECT では書けない「集計してから絞る」が、段階に分けるだけで書けるようになります。

未確認の製品があります そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database

未確認: IBM Db2

製品ごとの対応表(根拠つき)を見る

このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。

01副問い合わせの結果を表として使う

結論: FROM (SELECT …) 別名 と書くと、その結果が表になります。

ふつうの副問い合わせは WHERE の中に置いてとして使いますが、派生表は FROM に置いてとして使います。JOIN の相手にもできますし、そこから GROUP BY することもできます。

別名は必ず付けてください。 付けないと、その表の列を指すときに何と書けばよいのか読む人に伝わりません。製品によっては別名が無いとエラーになります。

⚠️ 派生表の中では、外側の列を参照できません。先に作って、あとから使うという順序です。

1 行ずつ、何をしているか
  1. SELECT b.busho_name, count(*) AS ninzu外側の問い合わせ
  2. FROM (SELECT * FROM shain★ここから派生表
  3. WHERE taishoku_on IS NULL) t★別名 t を必ず付ける
  4. JOIN busho b ON b.busho_id = t.busho_id作った表を結合の相手にできる
  5. GROUP BY b.busho_name作った表をまとめる
  6. ORDER BY count(*) DESC, b.busho_name;結果の並べ方

t は元からある表ではありません。この文の中だけで作られ、この文が終わると消えます。

在籍中の社員だけを部署ごとに数える
SELECT b.busho_name, count(*) AS ninzu
FROM (SELECT * FROM shain WHERE taishoku_on IS NULL) t
  JOIN busho b ON b.busho_id = t.busho_id
GROUP BY b.busho_name
ORDER BY count(*) DESC, b.busho_name;
busho_nameninzu
開発4
営業3
マーケ2
基盤2
管理2
経営1

6

6 行です。退職者 2 人を除いてから数えているので、開発は 4 人になっています。

02⚠️ よくある間違い:集計してから絞るには段階が要る

結論: WHERE に集約関数は書けません。 集計した結果で絞りたいなら、いったん表にしてから絞ります。

WHEREまとめる前に評価されるので、その時点では合計も平均もまだありません。書くとエラーになります。

方法は 2 つあります。

  • HAVING … まとめた直後に絞る。同じ 1 文の中で完結する
  • 派生表 … いったん集計結果を表にして、その外側で WHERE を使う

⚠️ どちらでも書けますが、さらに並べ替えや別の表との結合を重ねるなら派生表のほうが素直です。段階が目に見えるぶん、読む人が追いやすくなります。

見分け方は「その条件は、まとめた結果に対するものか」です。WHERE に書けるのは 1 行だけを見て決まる条件、HAVING と派生表の外側に書けるのはまとめた結果を見て決まる条件です。

もう 1 つ、派生表には外側で使える名前が付くという利点があります。内側で sum(salary) AS goukei と名付けておけば、外側では goukei と書くだけで済みます。集約関数をもう一度書く必要がありません。

WHERE で集計結果を絞ろうとする
SELECT busho_id, sum(salary) AS goukei
FROM shain
WHERE sum(salary) > 1500000
GROUP BY busho_id;
ERROR: aggregate functions are not allowed in WHERE

「aggregate functions are not allowed in WHERE」と出ます。書き方の間違いではなく、評価される順番の問題です。

集計してから、外側で絞る
SELECT busho_id, goukei
FROM (SELECT busho_id, sum(salary) AS goukei
      FROM shain
      GROUP BY busho_id) t
WHERE goukei > 1500000
ORDER BY goukei DESC;
busho_idgoukei
22215050
32131000
41617000

3

3 部署です。内側で部署ごとの合計を出して表にし、その外側の WHERE で 150 万円を超えるものだけに絞っています。

03同じ表を 2 回使うなら WITH を考える

結論: 派生表は書いた場所でしか使えません。 同じものを 2 か所で使いたいなら、WITH で名前を付けるほうが短くなります。

派生表を 2 か所に書くと、同じ SQL が 2 つ並ぶことになります。片方だけ直して食い違う事故が起きやすい形です。

一方で、1 か所でしか使わないなら派生表のほうが素直です。名前を先に定義してから本体を読む形より、その場に書いてあるほうが目で追いやすいこともあります。

⚠️ どちらが速いかは製品と状況によります。読みやすさで選んで、遅ければ両方試すのが現実的です。

作った表の列どうしを組み合わせる
SELECT t.busho_id, t.goukei, round(t.goukei / t.ninzu) AS hitori_atari
FROM (SELECT busho_id,
             sum(salary) AS goukei,
             count(*) AS ninzu
      FROM shain
      GROUP BY busho_id) t
ORDER BY hitori_atari DESC;
busho_idgoukeihitori_atari
1980000980000
22215050553763
41617000539000
51078000539000
32131000532750
7903000451500
NULL832000416000

7

7 行です。合計と人数を同時に作っておけば、その 2 つを使った計算が外側で自由にできます。所属未設定の 2 人も 1 つのまとまりとして出ています。

⚠️ この書き方は SQL Server / IBM Db2 では使えません(round(x)(引数 1 つ))。製品ごとの対応表

自分で打ってみる

このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。

押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。

解いてみる

読んだだけでは書けるようになりません。実行結果で採点します。

関連するトピック

根拠(一次情報)