WHERE status != 'CLOSED'、WHERE NOT (status = 'CLOSED') 皆無法使用 status 索引。WHERE status IN ('OPEN','PENDING','PROCESSING') 或 WHERE status = 'OPEN' OR status = 'PENDING' ...。| 特性 | IN 子查詢 | EXISTS 子查詢 |
|---|---|---|
| 執行順序 | 先執行子查詢,建臨時表/結果集,再驅動主查詢 | 先執行主查詢,逐列檢查子查詢是否存在(相關子查詢) |
| 索引利用 | 子查詢結果集可能無索引 | 子查詢 WHERE 條件列若有索引,極高效 |
| 適用場景 | 子查詢結果集小、靜態、可快取 | 子查詢關聯大表、需逐列過濾、索引完善 |
| NOT IN vs NOT EXISTS | NOT IN 含 NULL 時語義陷阱(全假),且無法用索引 | NOT EXISTS 安全、可用索引,強烈推薦 |
-- 低效 IN
SELECT * FROM orders WHERE cust_id IN (SELECT id FROM customers WHERE region='US');
-- 高效 EXISTS
SELECT * FROM orders o
WHERE EXISTS (SELECT 1 FROM customers c WHERE c.id = o.cust_id AND c.region='US');
SELECT 1 僅判斷存在性,不回傳實際資料,最輕量。
(a,b,c) 可支援:WHERE a=1、WHERE a=1 AND b=2、WHERE a=1 AND b=2 AND c=3。WHERE b=2、WHERE c=3、WHERE a=1 AND c=3(跳過 b)。ANALYZE TABLE (MySQL)、DBMS_STATS.GATHER_TABLE_STATS (Oracle)、UPDATE STATISTICS (SQL Server)。EXPLAIN (MySQL/Oracle)、SET SHOWPLAN_ALL ON (SQL Server),關注 type=ALL(全表掃描)、rows 巨大偏離實際、Using filesort、Using temporary。LIKE 'prefix%' 走索引,LIKE '%suffix' 全掃。ORDER BY 掛索引列,避免運算式。IN,NOT IN 改 NOT EXISTS。EXISTS,避免 IN 臨時表開銷。