☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
sql 中級

子查詢 Subquery

什麼是子查詢?

比喻:子查詢就像俄羅斯娃娃 🪆

打開一個查詢,裡面還有一個查詢—— 內層的查詢先執行,結果交給外層使用。


WHERE 裡的子查詢

回傳單一值

-- 找出分數高於平均的學生
SELECT Name, Score
FROM Students
WHERE Score > (                    -- ← 子查詢用括號包起來
    SELECT AVG(Score)              -- ← 這個先執行,算出平均分
    FROM Students                  -- ← 假設平均是 75
);                                 -- ← 外層變成 WHERE Score > 75

逐行解析:

WHERE Score > (                 -- 外層條件:分數要大於某個值
    SELECT AVG(Score)           -- 子查詢:先算出全班平均分(假設 75)
    FROM Students               -- 子查詢執行完畢,回傳 75
)                               -- 整句變成 WHERE Score > 75

回傳一組值(IN)

-- 找出有選「數學」的學生
SELECT Name
FROM Students
WHERE Id IN (                      -- ← IN 搭配回傳多值的子查詢
    SELECT StudentId
    FROM Enrollments
    WHERE CourseId = (
        SELECT Id FROM Courses WHERE Name = '數學'
    )
);

逐行解析:

-- 最內層先執行:找到數學的 CourseId(假設是 10)
SELECT Id FROM Courses WHERE Name = '數學'  -- → 10

-- 中間層執行:找出選了 CourseId=10 的學生 Id
SELECT StudentId FROM Enrollments WHERE CourseId = 10  -- → [1, 2]

-- 最外層執行:找出 Id 在 [1, 2] 裡的學生
SELECT Name FROM Students WHERE Id IN (1, 2)  -- → 小明, 小華

EXISTS — 檢查是否存在

-- 找出「有選過課」的學生
SELECT s.Name
FROM Students s
WHERE EXISTS (                     -- ← 只要子查詢有結果就通過
    SELECT 1                       -- ← SELECT 什麼不重要
    FROM Enrollments e
    WHERE e.StudentId = s.Id       -- ← 注意:用到了外層的 s.Id
);

逐行解析:

WHERE EXISTS (...)              -- 子查詢有回傳任何列 → true
    SELECT 1                    -- 只需要知道「有沒有」,不需要實際資料
    FROM Enrollments e
    WHERE e.StudentId = s.Id    -- 關聯子查詢:每個學生都會執行一次
                                -- 如果這個學生有選課記錄 → EXISTS = true
-- 找出「沒選過課」的學生
SELECT s.Name
FROM Students s
WHERE NOT EXISTS (
    SELECT 1
    FROM Enrollments e
    WHERE e.StudentId = s.Id
);

SELECT 裡的子查詢

-- 每個學生的分數 vs 全班平均
SELECT
    Name,
    Score,
    (SELECT AVG(Score) FROM Students) AS 全班平均,
    Score - (SELECT AVG(Score) FROM Students) AS 差距
FROM Students;

結果:

 Name | Score | 全班平均 | 差距
------+-------+---------+------
 小明 |    85 |   75.0  | 10.0
 小華 |    78 |   75.0  |  3.0
 小美 |    62 |   75.0  | -13.0

FROM 裡的子查詢(衍生表)

-- 先算出每個科系的平均分,再找出平均 > 80 的
SELECT *
FROM (
    SELECT Department, AVG(Score) AS avg_score
    FROM Students
    GROUP BY Department
) AS dept_stats                    -- ← 子查詢結果當作一張臨時表
WHERE avg_score > 80;              -- ← 外層再過濾

💡 FROM 裡的子查詢必須給別名(AS dept_stats)。


子查詢 vs JOIN

-- 子查詢寫法
SELECT Name FROM Students
WHERE Id IN (SELECT StudentId FROM Enrollments WHERE CourseId = 10);

-- JOIN 寫法(通常效能更好)
SELECT DISTINCT s.Name
FROM Students s
INNER JOIN Enrollments e ON s.Id = e.StudentId
WHERE e.CourseId = 10;
比較 子查詢 JOIN
可讀性 直覺,由內到外 需要理解連接邏輯
效能 簡單場景 OK 大量資料通常更快
適用場景 比較值、EXISTS 需要多表欄位

💡 能用 JOIN 就用 JOIN,效能通常更好。EXISTS 在檢查「有沒有」時效能很棒。

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊