CodeGym /課程 /SQL SELF /用 EXISTS 和 NOT EXISTS 檢查資料是否存在

用 EXISTS 和 NOT EXISTS 檢查資料是否存在

SQL SELF
等級 13, 課堂 3
開放

歡迎來到全新的 SQL 講座!今天要來聊聊 最不起眼但超強大的運算子——EXISTS 跟 NOT EXISTS。想像一下有個偵查兵,完全不留痕跡,但馬上回報你:「有,這裡有東西」或「沒有,這裡空空的」。這些運算子不會直接回傳資料,但可以讓你在查詢裡做出 超精確的邏輯判斷。

先從基礎開始。EXISTS 是個用來檢查 子查詢結果有沒有資料 的運算子。如果子查詢有回傳至少一筆資料,EXISTS 條件就會回傳 TRUE,不然就是 FALSE。

SELECT 1
WHERE EXISTS (
    SELECT * 
    FROM students 
    WHERE grade > 3.5
);

你看,其實我們根本不在乎子查詢裡的資料長怎樣,只在乎有沒有這樣的資料行。如果有一筆符合條件,查詢就會回傳 1。

EXISTS 的語法

EXISTS 的語法超簡單:

SELECT 欄位
FROM 資料表
WHERE EXISTS (
    SELECT 1
    FROM 另一個_資料表
    WHERE 條件
);

說明:

  • EXISTS 裡的子查詢可以是任何查詢。
  • 子查詢的結果會決定回傳 TRUE 還是 FALSE。

範例:有沒有學生分數大於 4?

假設有個 students 資料表:

id name grade
1 Otto 3.2
2 Anna 4.7
3 Dan 5.0
4 Lina 2.9

假設我們想檢查有沒有學生分數大於 4。可以用這個查詢:

SELECT '有學生分數很高喔!'
WHERE EXISTS (
    SELECT 1
    FROM students
    WHERE grade > 4
);

結果:

有學生分數很高喔!

為什麼 EXISTS 比 IN 快?

EXISTS 最大的優點就是 只要找到第一筆符合的資料就會停下來。所以如果你只是想檢查有沒有資料,EXISTS 超有效率。

舉例來說,假設 students 資料表有幾百萬筆資料,但我們只想知道有沒有一筆分數大於 4(grade > 4)。SQL 只要找到第一筆符合的資料,查詢就結束了。

使用 NOT EXISTS

現在來聊聊 NOT EXISTS。這個運算子就是 EXISTS 的反義詞。當 子查詢沒有回傳任何資料 時,它才會回傳 TRUE。

範例:找沒有分數(NULL)的學生

假設我們的資料表裡有些學生還沒拿到分數:

id name grade
1 Otto NULL
2 Anna 4.7
3 Dan 5.0
4 Lina NULL

我們想選出所有還沒拿到分數的學生。用 NOT EXISTS:

SELECT *
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM students 
    WHERE grade IS NOT NULL
    AND id = s.id
);

結果:

id name grade
1 Otto NULL
4 Lina NULL

EXISTS 跟 IN 的比較

有時候你會覺得 EXISTS 跟 IN 好像做一樣的事。表面上是這樣,但其實有細節。尤其當 NULL 混進來時,IN 的行為可能會讓你傻眼,這時 EXISTS 就是救星。

來看個例子。

courses 資料表(可以選的課程):

course_id name
1 數學
2 歷史

然後是學生:

student_id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

enrollments 資料表(誰選了哪門課):

student_id course_id
1 1
2 2
3 NULL

我們想選出有被選修的課程名稱。看起來很簡單。

用 IN:

SELECT name
FROM courses
WHERE course_id IN (
    SELECT course_id 
    FROM enrollments
);

表面上應該沒問題。但如果 enrollments 裡的 courseid 有 NULL,像 Maria Chi 那樣,IN 可能會回傳……什麼都沒有!因為 NULL 讓子查詢「不確定」,SQL 會想:「搞不好 NULL 就是我要找的那個 courseid?」

用 EXISTS:

SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
);

但 EXISTS 只會問:「有沒有一筆資料,course_id 一樣?」就這樣。它完全不怕有 NULL,因為它只找具體的對應,不是比對一個值清單。

結論:如果子查詢裡可能有 NULL,建議用 EXISTS,才不會被雷到。

實戰題目範例

students 資料表:

id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

enrollments 資料表:

student_id course_id
1 1
2 2
3 NULL

範例 1. 有選課的學生

來找找有出現在 enrollments 裡的學生(就算像 Maria Chi 那樣怪怪的也算):

SELECT name
FROM students s
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

結果:

Alex Lin
Anna Song
Maria Chi

只要學生有在 enrollments 裡出現過——不管 course_id 是不是 NULL——都會被選出來。

範例 2. 沒有選課的學生

現在來找還沒選課的學生:

SELECT name
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

結果:

Dan Seth
Shadow Moon

看來這兩位還沒找到想上的課。或是單純忘記要選課啦 :)

範例 3. 選課人數超過 5 人的課程

courses 資料表:

course_id name
1 數學
2 歷史
3 生物
4 哲學

enrollments 資料表:

student_id course_id
1 1
2 1
3 1
4 1
5 1
6 1
7 2
8 2
9 2
10 NULL

我們想找出 有超過五個學生選的課程。這時 EXISTS 就像在問:「這門課有沒有一組學生超過五個?」

SELECT name
FROM courses c
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE c.course_id = e.course_id
    GROUP BY e.course_id
    HAVING COUNT(*) > 5
);

結果:

數學

只有「數學」(course_id = 1)有六個學生選。其他課目前還沒這麼熱門。

用 EXISTS 和 NOT EXISTS 常見錯誤

  1. 搞錯子查詢語法。一定要確認子查詢有正確參照外部資料表。
  2. 忘記處理 NULL。就算用 EXISTS,有時還是要明確處理 NULL。
  3. 子查詢欄位沒加 index。這會讓查詢慢到爆。

今天就到這裡啦!現在你已經會用 EXISTS 跟 NOT EXISTS 來檢查資料存不存在,也知道它們跟 IN 的差別。下次講座我們會繼續深入子查詢,看看怎麼在 SELECT 裡處理聚合資料。

2
任務
SQL SELF, 等級 13, 課堂 3
上鎖
查找沒有成績的學生
查找沒有成績的學生
2
任務
SQL SELF, 等級 13, 課堂 3
上鎖
檢查有註冊學生的課程
檢查有註冊學生的課程
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION