例えば、君が大学のアナリストとして働いているとしよう(今、大学のデータベースを作ってるって覚えてる?)。学生とその成績を出すだけじゃなくて、グループ内の最高点も一緒に表示して、みんなの成績を簡単に比較できるようにしてほしいって頼まれたんだ。どうやってやる?もちろん、SELECTのサブクエリを使うんだよ!
SELECTのサブクエリを使うと、メインクエリの実行中に値を直接計算できる。これ、めっちゃ便利で、集計計算や複雑なフィルタ、他のデータコレクションも1つのクエリで組み合わせられるんだ。
SELECTのネストされたクエリの基本
SELECTのサブクエリは、文字通りそのまま:1つのSELECTの結果を別のSELECTの中に入れる感じ。これで、結果の各行ごとに追加の値を計算できる。
簡単な例を見てみよう。例えば、studentsテーブルがあって、こんな構造だとする:
| student_id | name | group_id |
|---|---|---|
| 1 | Linda | 101 |
| 2 | Otto | 102 |
| 3 | Anna | 101 |
そしてgradesテーブル:
| grade_id | student_id | grade |
|---|---|---|
| 1 | 1 | 5 |
| 2 | 1 | 4 |
| 3 | 2 | 3 |
| 4 | 3 | 5 |
| 5 | 3 | 4 |
例1:グループ内の最高点を追加する
やること:学生の名前と成績、さらにグループ内の最高点も表示して、各学生の成績がグループのベストとどれくらい違うか見えるようにする。
SQLコード:
SELECT
s.name AS student_name,
g.grade AS student_grade,
(
SELECT MAX(grade) -- このクエリは1つだけ値を返す
FROM grades
INNER JOIN students ON grades.student_id = students.student_id
WHERE students.group_id = s.group_id
) AS max_group_grade
FROM
students s
INNER JOIN
grades g ON s.student_id = g.student_id;
ここで何が起きてるか:
- 各学生について、名前と成績を取得(
s.name,g.grade)。 SELECT MAX(grade)— これはサブクエリで、その学生のグループ内の最高点を返す。- サブクエリはメインクエリの各行ごとに実行されて、
WHERE students.group_id = s.group_idでグループを絞ってる。
例2:グループの平均点
もっとアナリストに優しくしたい?最高点だけじゃなくて、グループの平均点も出力に追加しよう。
SQLコード:
SELECT
s.name AS student_name,
g.grade AS student_grade,
(
SELECT AVG(grade)
FROM grades
INNER JOIN students ON grades.student_id = students.student_id
WHERE students.group_id = s.group_id
) AS avg_group_grade
FROM
students s
INNER JOIN
grades g ON s.student_id = g.student_id;
今度は:
MAX()の代わりにAVG()を使って、グループの平均点を計算してる。- 「リアルタイム」なデータ分析ができる。
制限とアドバイス
SELECTのサブクエリは強力だけど、使い方には注意が必要:
- パフォーマンス。 サブクエリはメインクエリの各行ごとに実行される。テーブルが大きいとSQLクエリの実行が遅くなるかも。例えば、学生が1000人いたら、サブクエリも1000回実行される!
- インデックス。 サブクエリの
WHERE条件に使うカラムには、ちゃんとインデックスを貼っておくとクエリが速くなるよ。 - 読みやすさ。 ネストが深くなりすぎないように注意。サブクエリが複雑になったら、
FROMに移したり、一時テーブルを作るのもアリ。
使い方の例
他にも面白いケースをいくつか見てみよう。
例3:各学生の履修コース数
各学生が何コース履修してるかを表示するテーブルを出す。enrollmentsテーブルはstudent_idで学生とつながってる:
| student_id | course_id |
|---|---|
| 1 | 201 |
| 1 | 202 |
| 2 | 201 |
| 3 | 203 |
SQLコード:
SELECT
s.name AS student_name,
(
SELECT COUNT(*)
FROM enrollments
WHERE enrollments.student_id = s.student_id
) AS course_count
FROM
students s;
ここでは、サブクエリが各学生ごとにenrollmentsテーブルのレコード数を数えてる。
例4:各学生の「優等生」フラグ
学生が優等生かどうかを表示しよう。優等生の基準は、全ての成績が5であることとする。
SQLコード:
SELECT
s.name AS student_name,
(
SELECT CASE
WHEN MIN(g.grade) = 5 THEN '優等生'
ELSE '優等生じゃない'
END
FROM grades g
WHERE g.student_id = s.student_id
) AS status
FROM
students s;
ここではネストしたCASEを使って、全ての成績が5の学生だけに「優等生」ステータスをつけてる。
SELECTサブクエリの最適化
パフォーマンスが問題になることはすでに言ったよね。改善のためのヒントをいくつか:
- インデックスを使おう。 サブクエリでデータを絞るなら、使うカラムにインデックスを貼っておこう。
- 結果をキャッシュしよう。 サブクエリを
VIEWや一時テーブルに出しておくのもアリ。 - ネストは少なめに。 できるだけシンプルな方法を選んで、ネストしすぎないようにしよう。
SELECTのサブクエリは、計算やデータ分析の幅をめっちゃ広げてくれる。リソース食いだけど、ちゃんと最適化すればSQLがもっと表現力豊かで柔軟になるよ。だから、どんどん試して、自分なりのクエリ改善方法を見つけてみて!
GO TO FULL VERSION