CodeGym /コース /SQL SELF /複数の JOIN を1つのクエリで使う

複数の JOIN を1つのクエリで使う

SQL SELF
レベル 12 , レッスン 1
使用可能

イメージしてみて、データベースの各テーブルはでっかいパズルのピースみたいなもん。studentsテーブルは誰が勉強してるか、coursesテーブルは何を教えてるか、enrollmentsテーブルは誰がどのコースに登録してるかを知ってる。でもバラバラだと全体像は見えないよね?全部つなげて初めて絵が完成する。そこで登場するのが複数の JOINなんだ。

リアルな現場だと、データはよく関連テーブルに分けて整理されてる。こうすることで構造化できるし、無駄な重複も避けられる。例えば、うちの大学データベースにはこんなテーブルがある:

  • students — 学生のデータ。
  • enrollments — 学生がどのコースに登録してるかの情報。
  • courses — コースの情報。

もし学生、コース、先生の一覧を全部出したいなら、3つのテーブルをJOINでつなげる必要があるよ。

JOINの実行順序

複数のJOINを使うとき、PostgreSQLは左から右に順番に処理する。つまり、まず最初の2つのテーブルをつなげて、その結果を3つ目のテーブルとつなげて…って感じ。

例:

SELECT *
FROM students
    INNER JOIN enrollments ON students.id = enrollments.student_id
    INNER JOIN courses ON enrollments.course_id = courses.id;
  1. まずstudentsenrollmentsstudents.id = enrollments.student_idでつなげる。
  2. その結果を使って、enrollments.course_id = courses.idcoursesとつなげる。

特に大きなテーブルを扱うときは、この順番が超重要。JOINの構造がイケてないと、パフォーマンスがめっちゃ落ちることもある。

例:学生・コース・先生の一覧

例えば、こんなデータのテーブルがあるとする:

studentsテーブル:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

coursesテーブル:

id name teacher
101 Mathematics Ellen Moore
102 Physics James Okoro
103 Computer Science Nina Delgado

enrollmentsテーブル:

student_id course_id
1 101
1 103
2 102
3 101

クエリ:

SELECT
    students.name AS student_name,
    courses.name AS course_name,
    courses.teacher AS teacher_name
FROM students
    INNER JOIN enrollments ON students.id = enrollments.student_id
    INNER JOIN courses ON enrollments.course_id = courses.id;

結果:

student_name course_name teacher_name
Otto Song Mathematics Ellen Moore
Otto Song Computer Science Nina Delgado
Maria Chi Physics James Okoro
Alex Lin Mathematics Ellen Moore

複数JOINクエリでのフィルタリング

JOINを使ったクエリでも、条件で絞り込んで返すデータ量を減らしたり、クエリを速くしたりできる。例えば、「Mathematics」コースを受講してる学生だけ出したい場合:

SELECT
    students.name AS student_name,
    courses.name AS course_name
FROM students
    INNER JOIN enrollments ON students.id = enrollments.student_id
    INNER JOIN courses ON enrollments.course_id = courses.id
WHERE courses.name = 'Mathematics';

結果:

student_name course_name
Otto Song Mathematics
Alex Lin Mathematics

複数JOINクエリの最適化

大きなテーブルを扱うなら、クエリの最適化はめっちゃ大事。コツをいくつか紹介するね:

  1. インデックスを使う

インデックスがあると、PostgreSQLは特にキー項目でのJOINが速くなる。enrollmentsテーブルのstudent_idcourse_idにはインデックスを作っておこう。

インデックス作成例:

CREATE INDEX idx_enrollments_student_id ON enrollments(student_id);
CREATE INDEX idx_enrollments_course_id ON enrollments(course_id);

インデックスについては次のレベルで詳しくやるけど、ここでもちょっと触れておくね。JOINとめっちゃ関係あるから。

  1. 早い段階でデータを絞り込む

WHERE条件を使って、JOINする前に処理する行数を減らそう。例えば:

SELECT
    students.name AS student_name,
    courses.name AS course_name
FROM students
    INNER JOIN enrollments ON students.id = enrollments.student_id
    INNER JOIN courses ON enrollments.course_id = courses.id
WHERE 
    courses.teacher = 'Ivan Petrov';
  1. JOINする行数を最小限にする

2つのテーブルの全レコードをJOINするんじゃなくて、まずサブクエリで絞り込んでからJOINしよう:

SELECT
    students.name AS student_name,
    courses.name AS course_name
FROM 
    (SELECT * FROM students WHERE id IN (1, 2)) sub_students
INNER JOIN enrollments ON sub_students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id;

サブクエリ(ネストしたSELECT)については、次のレベルですぐやるからお楽しみに :P

複雑なJOIN例:学生・コース・学部

さらにfacultiesテーブルが追加されたとしよう:

facultiesテーブル:

id name
10 Engineering
20 Natural Sciences

coursesテーブルが更新:

id name teacher faculty_id
101 Mathematics Ellen Moore 10
102 Physics James Okoro 20
103 Computer Science Nina Delgado 10

学生・コース・学部の一覧を出すには、さらにJOINを追加しよう:

SELECT
    students.name AS student_name,
    courses.name AS course_name,
    faculties.name AS faculty_name
FROM students
    INNER JOIN enrollments ON students.id = enrollments.student_id
    INNER JOIN courses ON enrollments.course_id = courses.id
    INNER JOIN faculties ON courses.faculty_id = faculties.id;

結果:

student_name course_name faculty_name
Otto Song Mathematics Engineering
Otto Song Computer Science Engineering
Maria Chi Physics Natural Sciences
Alex Lin Mathematics Engineering

複数JOINのSQLクエリはちょっと難しそうだけど、うまく使えば強力なレポートや有益な情報がゲットできる。ちゃんと最適化&構造化すれば、大規模データベースでもバッチリ使える武器になるよ!

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION