想像一下你現在是大學的分析師(我們不是在做大學的資料庫嗎,還記得吧?)。你被要求不只是列出學生和他們的成績,還要在表格裡加一欄,顯示該組的最高分,這樣就能輕鬆比較每個人的表現。這要怎麼做?當然就是用 SELECT 裡的子查詢啦!
SELECT 裡的子查詢可以讓你在主查詢執行時直接算出值。這超方便,因為你可以在一個查詢裡組合聚合運算、複雜的篩選,甚至拉進其他資料集合。
SELECT 裡巢狀查詢的基礎
在 SELECT 裡的子查詢就像字面意思一樣:你把一個 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) -- 這個查詢只會回傳一個值
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;
現在:
- 我們用
AVG()取代MAX(),來算組內平均分。 - 這樣就能「即時」分析資料。
限制與建議
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