もっと深くラビットホールに潜ってみよう!今回はFROM構文でサブクエリを使う方法を見ていくよ。これはSQLエンジニアがよく使うテクで、その場で強力な一時テーブルを作って、まるでDBに存在してるかのように再利用できるんだ。
例えば、レポートを作る時に計算やグループ化、データのフィルタリングが必要だけど、サーバーに一時テーブルを作りたくない場合、どうする?そこでFROMのサブクエリが登場!これを使うと:
- データを一時的にまとめたり、集計したりしてからメインクエリに渡せる。
- その場で構造化されたデータセットを作れる。
- DBに余計な中間データを保存せずに、操作回数を減らせる。
FROMのサブクエリは、メインクエリで使えるミニテーブルみたいなもの。モジュールを組み立てる感じで、速くて柔軟、しかも余計なコストもかからないよ :)
FROMサブクエリの基本
FROMサブクエリでは、サブクエリを使って一時テーブル(またはサブテーブル)を作り、それを全体のクエリの一部にするんだ。やることは3つ:
- サブクエリを
FROM構文の中でカッコで囲んで書く。 - サブクエリにエイリアス(別名)をつける。
- そのエイリアスを、普通のテーブルみたいに使う。
シンタックス
SELECT カラム
FROM (
SELECT カラム
FROM テーブル
WHERE 条件
) AS エイリアス
WHERE 外部条件;
ちょっと難しそう?じゃあ、例を見てみよう!
例:学生と平均点
例えば、こんな2つのテーブルがあるとする:
students(学生データ — 名前とID):
| student_id | student_name |
|---|---|
| 1 | Alex |
| 2 | Anna |
| 3 | Dan |
grades(学生の成績データ):
| grade_id | student_id | grade |
|---|---|---|
| 1 | 1 | 80 |
| 2 | 1 | 85 |
| 3 | 2 | 90 |
| 4 | 3 | 70 |
| 5 | 3 | 75 |
さて、やりたいことは:学生ごとの平均点リストを取得すること。
まずは、各学生の平均点を計算するサブクエリを書いて、それをメインクエリで使ってみよう。
SELECT s.student_name, g.avg_grade
FROM (
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
) AS g
JOIN students AS s ON s.student_id = g.student_id;
結果:
| student_name | avg_grade |
|---|---|
| Alex | 82.5 |
| Anna | 90.0 |
| Dan | 72.5 |
その場で一時テーブルを作る
FROMのサブクエリは、データ処理を複数段階でやりたい時に特に便利。例えば、平均点だけじゃなくて、各学生の最高点も一緒に計算したい場合、全部1つのクエリでできる!
SELECT g.student_id, g.avg_grade, g.max_grade
FROM (
SELECT student_id,
AVG(grade) AS avg_grade,
MAX(grade) AS max_grade
FROM grades
GROUP BY student_id
) AS g;
結果:
| student_id | avg_grade | max_grade |
|---|---|---|
| 1 | 82.5 | 85 |
| 2 | 90 | 90 |
| 3 | 72.5 | 75 |
これ、ちゃんとavg_gradeやmax_gradeみたいなカラムを持つ一時テーブルとして動いてるよ。
FROMサブクエリはどんな時に使うといい?
集計データが欲しい時。 まず計算(平均、合計、最大値など)をしてから、他のテーブルと結合したい場合。
データをフィルタしたい時。 メインテーブルと結合する前にデータを絞り込みたい時。
複雑なクエリをシンプルにしたい時。 複雑な処理を段階的に分けると、混乱しにくいよ。
例:2段階処理の学生レポート
例えば、平均点が80より高い学生だけを探したいとしよう。まず平均点を計算するサブクエリを書いて、それをフィルタに使う。
SELECT s.student_name, g.avg_grade
FROM students AS s
JOIN (
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
) AS g ON s.student_id = g.student_id
WHERE g.avg_grade > 80;
結果:
| student_name | avg_grade |
|---|---|
| Alex | 82.5 |
| Anna | 90.0 |
使う時のポイントとアドバイス
エイリアスは必須。 サブクエリには必ずエイリアス(例:AS g)をつけよう。じゃないとPostgreSQLはその「一時テーブル」をどう呼べばいいか分からなくなる。
最適化。 FROMサブクエリは、テーブルのJOINより遅くなることがある。特にサブクエリ内でフィルタしてる場合は注意。
インデックス。 結合やフィルタに使うカラムにはインデックスを貼っておこう。パフォーマンスにめっちゃ影響するよ。
複雑なクエリ例:コースごとの学生数
じゃあ、もうちょっとリアルな課題をやってみよう。例えば、こんなテーブルがある:
courses(コース一覧):
| course_id | course_name |
|---|---|
| 1 | SQL Basics |
| 2 | Python Basics |
そしてenrollments(学生のコース登録データ):
| student_id | course_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
さて、各コースに何人の学生が登録しているか知りたい。
SELECT c.course_name, e.students_count
FROM courses AS c
JOIN (
SELECT course_id, COUNT(student_id) AS students_count
FROM enrollments
GROUP BY course_id
) AS e ON c.course_id = e.course_id;
結果:
| course_name | students_count |
|---|---|
| SQL Basics | 2 |
| Python Basics | 1 |
このレクチャー、気に入ってくれたら嬉しいな…次回はもっと面白い内容になるよ :)
GO TO FULL VERSION