EXISTS 運算子 (SQL EXISTS Operator)

EXISTS 運算子用於檢查子查詢是否有返回任何記錄。如果子查詢返回至少一筆記錄EXISTSTRUE;如果子查詢返回空結果集EXISTSFALSE

EXISTS 常用於相關子查詢中,檢查外部查詢的記錄是否在另一個表中有對應的關聯記錄。

EXISTS 語法 (Syntax)

SELECT column_list
FROM table_name
WHERE EXISTS (SELECT 1 FROM related_table WHERE condition);

💡 提示: 在 EXISTS 子查詢中,SELECT 後面的欄位不重要 (因為只檢查是否有記錄存在),通常使用 SELECT 1SELECT *

NOT EXISTS

NOT EXISTSEXISTS 的相反,當子查詢沒有返回任何記錄時為 TRUE

SELECT column_list
FROM table_name
WHERE NOT EXISTS (SELECT 1 FROM related_table WHERE condition);

EXISTS 用法範例 (Example)

customers 資料表:

C_IdNameCity
1張一台北市
2王二新竹縣
3李三高雄縣

orders 資料表:

O_IdOrder_NoC_Id
125723
273753
375201

範例 1:找出有訂單的客戶

SELECT * FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o
    WHERE o.C_Id = c.C_Id
);

結果:

C_IdNameCity
1張一台北市
3李三高雄縣

範例 2:找出沒有訂單的客戶

SELECT * FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o
    WHERE o.C_Id = c.C_Id
);

結果:

C_IdNameCity
2王二新竹縣

EXISTS vs IN

EXISTSIN 在很多情況下可以達到相同的結果,但它們的運作方式不同:

使用 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);

主要差異

特性EXISTSIN
運作方式對外部每筆記錄執行子查詢,找到匹配即停止先執行子查詢取得所有結果,再比對
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 的優化特性

  1. 短路求值 (Short-circuit evaluation):一旦找到匹配的記錄,子查詢就會停止執行。
  2. 只檢查存在性:不需要返回實際資料,減少資料傳輸。

適用場景

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'
);

相關主題