⭐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

SELECT DISTINCT TRIM(s.code) AS code FROM source_list s WHERE s.code IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM target_list t WHERE TRIM(t.code) = TRIM(s.code) AND t.code IS NOT NULL ) ORDER BY code;

為什麼我加了 TRIM?

因為很多系統資料會出現:

  • 前後空白(例如 "ABC "

  • 使用者輸入或匯入檔案造成的空格差異

如果你不 TRIM,你會遇到「看起來一樣但 SQL 比對不到」的怪問題。


三、為什麼不建議新手用 NOT IN?(超常見踩雷)

很多人第一直覺會寫:

SELECT DISTINCT s.code FROM source_list s WHERE s.code NOT IN (SELECT t.code FROM target_list t);

看起來合理,但在 Oracle 很容易出事。

NOT IN 的最大問題:只要子查詢出現 NULL,整個結果可能變空集合

換句話說:

target_list 只要有任何一筆 code = NULL
你的 NOT IN 可能就「什麼都查不到」。

這就是 SQL 三值邏輯(TRUE / FALSE / UNKNOWN)帶來的陷阱。
所以實務上做「反查 / 缺漏比對」,NOT EXISTS 更可靠


四、另一種常見寫法:LEFT JOIN + IS NULL(可讀性高)

如果你覺得 NOT EXISTS 看不懂,LEFT JOIN 版也很常用:

SELECT DISTINCT TRIM(s.code) AS code FROM source_list s LEFT JOIN target_list t ON TRIM(t.code) = TRIM(s.code) WHERE s.code IS NOT NULL AND t.code IS NULL ORDER BY code;

這段的意思是什麼?

  • LEFT JOIN:把 A 的每筆資料都保留

  • 若 B 找不到對應的資料 → B 的欄位會變 NULL

  • 所以 t.code IS NULL 就代表「A 有但 B 沒有」

可讀性很好,除錯時很方便
但大資料量下,很多情況 NOT EXISTS 效能更穩。


五、實務小抄:你可以套用到任何「版本/清單/代碼」比對

只要你的需求是:

  • A 有,B 沒有

  • 以某個欄位當 Key 比對(例如 code、version、id)

你就可以套用這個模板:

SELECT DISTINCT <清理後的A.key> FROM A WHERE A.key IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM B WHERE B.key = A.key AND B.key IS NOT NULL );

建議加上資料清理(TRIM / UPPER)

  • 有空白問題 → TRIM()

  • 有大小寫問題 → UPPER(TRIM())

例如:

WHERE UPPER(TRIM(b.key)) = UPPER(TRIM(a.key))

六、常見 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 的規則
    我就能幫你產出可直接上線的版本(含效能建議與索引方向)。

留言

這個網誌中的熱門文章

🔍Vue.js 專案錯誤排查:解決 numericFields is not defined 與合併儲存格邏輯最佳化

🖥️遠端桌面連線完整新手指南:Windows RDP、Chrome Remote Desktop、AnyDesk、TeamViewer 一次搞懂

🔎EF Core 連 Oracle 出現 ORA-00600 [kpp_concatq:2] 的完整排錯指南(含 EF Core ToString/CultureInfo 錯誤)