EXCEPT 運算子 (SQL EXCEPT Operator)
EXCEPT 運算子用於返回存在於第一個查詢結果但不存在於第二個查詢結果中的記錄,也就是兩個結果集的差集。
簡單來說:A EXCEPT B = A 減去 (A 與 B 的交集)
EXCEPT 語法 (Syntax)
SELECT column_list FROM table1
EXCEPT
SELECT column_list FROM table2;
使用 EXCEPT 的條件:
- 每個 SELECT 語句必須有相同數量的欄位。
- 對應的欄位必須有相容的資料型別。
- 結果集會自動去除重複記錄。
EXCEPT 用法範例 (Example)
products_taiwan 資料表 (台灣地區銷售產品):
| P_Id | P_Name |
|---|---|
| 1 | LCD |
| 2 | CPU |
| 3 | RAM |
products_china 資料表 (中國大陸地區銷售產品):
| P_Id | P_Name |
|---|---|
| 1 | Keyboard |
| 2 | CPU |
| 3 | LCD |
範例: 查詢在台灣有銷售但在中國沒有銷售的產品。
SELECT P_Name FROM products_taiwan
EXCEPT
SELECT P_Name FROM products_china;
結果:
| P_Name |
|---|
| RAM |
因為 LCD 和 CPU 在 products_china 中也存在,所以被排除了,只剩下 RAM。
EXCEPT 運作圖解
products_taiwan: products_china:
┌─────────────────┐ ┌─────────────────┐
│ LCD, CPU, RAM │ │ Keyboard, CPU, │
│ │ │ LCD │
└─────────────────┘ └─────────────────┘
A EXCEPT B:返回 A 中有但 B 中沒有的
┌─────────────────┐
│ RAM │ ← 只有台灣有
└─────────────────┘
EXCEPT 的方向性
⚠️ 重要: EXCEPT 是有方向性的!
A EXCEPT B和B EXCEPT A的結果不同。
範例: 查詢在中國有銷售但在台灣沒有銷售的產品 (相反方向)。
SELECT P_Name FROM products_china
EXCEPT
SELECT P_Name FROM products_taiwan;
結果:
| P_Name |
|---|
| Keyboard |
這次返回的是 Keyboard,因為它只存在於中國的產品表中。
EXCEPT vs MINUS
EXCEPT 和 MINUS 功能完全相同,只是不同資料庫使用不同的關鍵字:
| 資料庫 | 使用的關鍵字 |
|---|---|
| SQL Server | EXCEPT |
| PostgreSQL | EXCEPT |
| SQLite | EXCEPT |
| Oracle | MINUS |
| MySQL | 不支援 (需使用替代方案) |
MySQL 中的替代方案
⚠️ 注意: MySQL 不支援
EXCEPT運算子。但可以使用其他方法達到相同效果。
方法 1:使用 NOT IN 子查詢
SELECT P_Name
FROM products_taiwan
WHERE P_Name NOT IN (SELECT P_Name FROM products_china);
方法 2:使用 LEFT JOIN + IS NULL
SELECT t.P_Name
FROM products_taiwan t
LEFT JOIN products_china c ON t.P_Name = c.P_Name
WHERE c.P_Name IS NULL;
方法 3:使用 NOT EXISTS
SELECT P_Name
FROM products_taiwan t
WHERE NOT EXISTS (
SELECT 1 FROM products_china c WHERE c.P_Name = t.P_Name
);
💡 效能提示: 在大型資料集上,
NOT EXISTS通常比NOT IN效能更好,尤其是當子查詢結果可能包含 NULL 值時。
實際應用場景
1. 找出流失的客戶
找出去年有購買但今年沒有購買的客戶:
SELECT Customer_Id FROM orders_2023
EXCEPT
SELECT Customer_Id FROM orders_2024;
2. 資料比對與差異分析
找出舊系統中有但新系統中沒有的記錄 (可能漏掉的遷移資料):
SELECT Product_Code FROM old_system_products
EXCEPT
SELECT Product_Code FROM new_system_products;
3. 權限檢查
找出沒有特定權限的使用者:
SELECT User_Id FROM all_users
EXCEPT
SELECT User_Id FROM admin_users;
4. 庫存管理
找出尚未被訂購過的產品:
SELECT Product_Id FROM products
EXCEPT
SELECT DISTINCT Product_Id FROM order_details;
EXCEPT 與 NULL 值的處理
當比較包含 NULL 值的欄位時,EXCEPT 會將 NULL 視為相等的值:
-- 假設 table1 有 NULL,table2 也有 NULL
-- EXCEPT 會認為它們相同,不會返回在結果中
這與一般的 SQL 比較 (NULL = NULL 返回 UNKNOWN) 不同。
集合運算總結
| 運算子 | 說明 | 圖示概念 |
|---|---|---|
| UNION | A 或 B 中的記錄 | A ∪ B |
| INTERSECT | 同時在 A 和 B 中的記錄 | A ∩ B |
| EXCEPT | 在 A 中但不在 B 中的記錄 | A - B |