EXISTS 運算子 (SQL EXISTS Operator)
EXISTS 運算子用於檢查子查詢是否有返回任何記錄。如果子查詢返回至少一筆記錄,EXISTS 為 TRUE;如果子查詢返回空結果集,EXISTS 為 FALSE。
EXISTS 常用於相關子查詢中,檢查外部查詢的記錄是否在另一個表中有對應的關聯記錄。
EXISTS 語法 (Syntax)
SELECT column_list
FROM table_name
WHERE EXISTS (SELECT 1 FROM related_table WHERE condition);
💡 提示: 在 EXISTS 子查詢中,
SELECT後面的欄位不重要 (因為只檢查是否有記錄存在),通常使用SELECT 1或SELECT *。
NOT EXISTS
NOT EXISTS 是 EXISTS 的相反,當子查詢沒有返回任何記錄時為 TRUE。
SELECT column_list
FROM table_name
WHERE NOT EXISTS (SELECT 1 FROM related_table WHERE condition);
EXISTS 用法範例 (Example)
customers 資料表:
| C_Id | Name | City |
|---|---|---|
| 1 | 張一 | 台北市 |
| 2 | 王二 | 新竹縣 |
| 3 | 李三 | 高雄縣 |
orders 資料表:
| O_Id | Order_No | C_Id |
|---|---|---|
| 1 | 2572 | 3 |
| 2 | 7375 | 3 |
| 3 | 7520 | 1 |
範例 1:找出有訂單的客戶
SELECT * FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.C_Id = c.C_Id
);
結果:
| C_Id | Name | City |
|---|---|---|
| 1 | 張一 | 台北市 |
| 3 | 李三 | 高雄縣 |
範例 2:找出沒有訂單的客戶
SELECT * FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.C_Id = c.C_Id
);
結果:
| C_Id | Name | City |
|---|---|---|
| 2 | 王二 | 新竹縣 |
EXISTS vs IN
EXISTS 和 IN 在很多情況下可以達到相同的結果,但它們的運作方式不同:
使用 IN:
SELECT * FROM customers
WHERE C_Id IN (SELECT C_Id FROM orders);
使用 EXISTS:
SELECT * FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.C_Id = c.C_Id);
主要差異
| 特性 | EXISTS | IN |
|---|---|---|
| 運作方式 | 對外部每筆記錄執行子查詢,找到匹配即停止 | 先執行子查詢取得所有結果,再比對 |
| NULL 處理 | 正確處理 | NOT IN 遇到 NULL 可能有問題 |
| 效能 | 外表小、內表大且有索引時較優 | 子查詢結果集小時較優 |
NOT IN 的 NULL 陷阱
-- 假設 orders 表中有一筆 C_Id 為 NULL 的記錄
-- 這個查詢可能返回空結果!
SELECT * FROM customers
WHERE C_Id NOT IN (SELECT C_Id FROM orders);
-- 使用 NOT EXISTS 可以正確運作
SELECT * FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.C_Id = c.C_Id);
⚠️ 建議: 當使用
NOT IN時,確保子查詢結果不包含NULL,或改用NOT EXISTS。
EXISTS 的效能考量
EXISTS 的優化特性
- 短路求值 (Short-circuit evaluation):一旦找到匹配的記錄,子查詢就會停止執行。
- 只檢查存在性:不需要返回實際資料,減少資料傳輸。
適用場景
EXISTS 較優的情況:
- 外部表較小,內部表較大
- 內部表有適當的索引
- 只需要檢查存在性,不需要返回關聯表的資料
IN 較優的情況:
- 子查詢結果集很小且可快速執行
- 不需要與外部查詢關聯
實際應用範例
1. 找出有評論的產品
SELECT * FROM products p
WHERE EXISTS (
SELECT 1 FROM reviews r
WHERE r.Product_Id = p.P_Id
);
2. 找出沒有被任何訂單購買的產品
SELECT * FROM products p
WHERE NOT EXISTS (
SELECT 1 FROM order_details od
WHERE od.Product_Id = p.P_Id
);
3. 找出在多個城市都有訂單的客戶
SELECT * FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o1
WHERE o1.C_Id = c.C_Id
AND EXISTS (
SELECT 1 FROM orders o2
WHERE o2.C_Id = c.C_Id
AND o1.Ship_City <> o2.Ship_City
)
);
4. 條件性更新
-- 只更新有訂單的客戶的狀態
UPDATE customers c
SET Status = 'Active'
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.C_Id = c.C_Id
AND o.Order_Date > '2024-01-01'
);