⭐Oracle SQL 教學:如何找出「A 表有但 B 表沒有」的資料?用 NOT EXISTS 避開 NULL 陷阱(新手也懂)
在日常工作中,只要你有「資料要對帳 / 比對 / 補漏」的需求,就一定會遇到這種情境:
系統 A 有一份清單、系統 B 也有一份清單
我想找出:A 裡面有,但 B 裡面沒有 的那幾筆。
例如:
-
倉庫有商品清單(A),網站有上架清單(B) → 找出「有庫存但沒上架」的商品
-
訂單明細(A),發票明細(B) → 找出「有訂單但沒開發票」
-
版本清單(A),報表清單(B) → 找出「有版本但缺報表」
這篇文章用最白話的方式帶你理解,並提供 Oracle SQL 的安全寫法,避免新手最常踩的 NULL 陷阱。
一、問題翻譯成 SQL:什麼叫「A 有、B 沒有」?
假設我們有兩張表:
-
SOURCE_LIST:來源清單(A) -
TARGET_LIST:目標清單(B)
它們都有一個共同欄位:CODE
你的需求是:
找出所有 出現在 SOURCE_LIST.CODE,但是 沒有出現在 TARGET_LIST.CODE 的代碼。
二、最推薦寫法:NOT EXISTS(安全、穩定、效能好)
在 Oracle 裡,最穩定的寫法通常是
NOT EXISTS:
為什麼我加了 TRIM?
因為很多系統資料會出現:
-
前後空白(例如
"ABC ") -
使用者輸入或匯入檔案造成的空格差異
如果你不 TRIM,你會遇到「看起來一樣但 SQL 比對不到」的怪問題。
三、為什麼不建議新手用 NOT IN?(超常見踩雷)
很多人第一直覺會寫:
看起來合理,但在 Oracle 很容易出事。
NOT IN 的最大問題:只要子查詢出現 NULL,整個結果可能變空集合
換句話說:
target_list只要有任何一筆code = NULL,
你的NOT IN可能就「什麼都查不到」。
這就是 SQL 三值邏輯(TRUE / FALSE / UNKNOWN)帶來的陷阱。
所以實務上做「反查 / 缺漏比對」,NOT EXISTS 更可靠。
四、另一種常見寫法:LEFT JOIN + IS NULL(可讀性高)
如果你覺得
NOT EXISTS 看不懂,LEFT JOIN
版也很常用:
這段的意思是什麼?
-
LEFT JOIN:把 A 的每筆資料都保留 -
若 B 找不到對應的資料 → B 的欄位會變
NULL -
所以
t.code IS NULL就代表「A 有但 B 沒有」
可讀性很好,除錯時很方便
但大資料量下,很多情況NOT EXISTS效能更穩。
五、實務小抄:你可以套用到任何「版本/清單/代碼」比對
只要你的需求是:
-
A 有,B 沒有
-
以某個欄位當 Key 比對(例如 code、version、id)
你就可以套用這個模板:
建議加上資料清理(TRIM / UPPER)
-
有空白問題 →
TRIM() -
有大小寫問題 →
UPPER(TRIM())
例如:
六、常見 QA(新手最常問)
Q1:為什麼要 SELECT DISTINCT?
因為 A 表可能同一個 key 出現多次,你只想要「缺漏的 key 清單」,而不是重複列出。
Q2:為什麼要 ORDER BY?
如果要給前端下拉選單或報表用,排序能讓畫面穩定、使用者好找。
Q3:NOT EXISTS 一定比較快嗎?
不一定,但在 Oracle 中做「反查」時,NOT EXISTS
通常是最穩健的選擇。最終仍建議看執行計畫與索引。
結語:反查缺漏,比你想得更常用
「A 有但 B 沒有」是資料庫工作最常見的需求之一。
只要你掌握 NOT EXISTS,就能避開
NULL
的坑,寫出更可靠、更容易上線的 SQL。
如果你希望我幫你把這個寫法套用到你的實際情境(例如版本清單、報表清單、計畫資料等等),你只要提供:
-
兩張表的「代稱」與 key 欄位名稱(可匿名)
-
需要 TRIM / UPPER 的規則
我就能幫你產出可直接上線的版本(含效能建議與索引方向)。
留言
張貼留言