小表驅動大表:讓較小的結果集作為驅動表(外層迴圈),大表作為被驅動表(內層查找)。
| 語法 | 等價邏輯 | 優勢場景 |
|---|---|---|
SELECT * FROM A WHERE id IN (SELECT id FROM B) |
FOR each row in B: SELECT * FROM A WHERE A.id = B.id |
B(子查詢)結果集 小於 A 時 |
SELECT * FROM A WHERE EXISTS (SELECT 1 FROM B WHERE B.id = A.id) |
FOR each row in A: SELECT * FROM B WHERE B.id = A.id |
A(主查詢)結果集 小於 B 時 |
-- rocky_member 大表,rocky_vip_appro 小表(已過濾 passed=1)
-- 使用 EXISTS:0.313s
SELECT SQL_NO_CACHE * FROM rocky_member m
WHERE EXISTS (SELECT 1 FROM rocky_vip_appro a
WHERE m.ID = a.user_id AND a.passed = 1);
-- 使用 IN:0.160s(快近一倍)
SELECT SQL_NO_CACHE * FROM rocky_member m
WHERE m.ID IN (SELECT ID FROM rocky_vip_appro WHERE passed = 1);
結論:子查詢表(rocky_vip_appro)資料量較小,IN 將其作為驅動表更高效。
ID/user_id)必須建立索引,否則全表掃描導致效能崩塌。NOT IN / NOT EXISTS 同理,但 NOT IN 若子查詢含 NULL 會導致結果為空,NOT EXISTS 較安全。針對大量資料的 NOT IN,建議改用 LEFT JOIN ... IS NULL 或 NOT EXISTS,並確保索引覆蓋,避免「天文數字」級的嵌套迴圈比較。
子查詢小 → 用 IN
主查詢小 → 用 EXISTS
關鍵欄位必加索引
NOT IN 慎用,優先 NOT EXISTS / LEFT JOIN