想像一下,你資料庫裡的每一張 table 就像拼圖的一塊。學生 table 知道 誰在唸書,課程 table 負責 教什麼,而選課紀錄 table 則記著 誰選了什麼課。但這些拼圖單獨看都沒啥意思。想看全貌?就得把它們拼起來——這時候 多重 JOIN 就派上用場啦!
現實中,資料常常會拆成多個關聯 table,這樣才有結構又不會重複。像我們這個大學資料庫就有這幾張 table:
students— 學生資料。enrollments— 學生選課紀錄。courses— 課程資訊。
如果你想查出所有學生、他們的課程還有老師,就得用 JOIN 把這三張 table 串起來。
JOIN 的執行順序
當你用多個 JOIN 時,PostgreSQL 會從左到右處理。也就是說,先把前兩張 table 合起來,再把結果跟第三張 table 合,依此類推。
舉個例子:
SELECT *
FROM students
INNER JOIN enrollments ON students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id;
- 先把
students跟enrollments用students.id = enrollments.student_id合起來。 - 然後用第一步的結果,跟
courses以enrollments.course_id = courses.id再合一次。
這個順序很重要,尤其是 table 很大的時候。如果 JOIN 寫得不對,效能會掉超多。
範例:學生、課程和老師清單
假設我們有這些資料表:
students table:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
courses table:
| id | name | teacher |
|---|---|---|
| 101 | Mathematics | Ellen Moore |
| 102 | Physics | James Okoro |
| 103 | Computer Science | Nina Delgado |
enrollments table:
| 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 查詢的優化
面對大 table,查詢優化超重要。這裡有幾個小撇步:
- 用 index
index 讓 PostgreSQL 查資料快很多,特別是用在 key 欄位的時候。記得在 enrollments 的 student_id 跟 course_id 上都要有 index。
建立 index 的範例:
CREATE INDEX idx_enrollments_student_id ON enrollments(student_id);
CREATE INDEX idx_enrollments_course_id ON enrollments(course_id);
index 之後會再講更多,不過這裡先提一下,因為 JOIN 常常會用到。
- 一開始就先篩掉不要的資料
用 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 = '伊凡 彼得羅夫';
- 減少要 JOIN 的資料量
不要直接把兩張 table 全部資料都 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;
子查詢(subquery)之後馬上就會學到啦 :P
進階範例:學生、課程和學院
假設我們又多了一張 faculties table:
faculties table:
| id | name |
|---|---|
| 10 | Engineering |
| 20 | Natural Sciences |
courses table 更新:
| 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 查詢雖然有點複雜,但你可以用它們做出很強大的報表,挖出很多有用的資訊。只要優化和結構寫得好,它們就是你玩大資料庫的神兵利器!
GO TO FULL VERSION