EXPLAIN SELECT * FROM vendor WHERE EXISTS (SELECT * FROM area WHERE area_code = vendor_prov_code) LIMIT 10;
執行流程:
vendor 讀取一行vendor_prov_code 代入子查詢TRUE/FALSETRUE 保留該行,FALSE 捨棄關鍵特點:子查詢不返回實際數據,僅返回布爾值;可利用外層表索引。
EXPLAIN SELECT * FROM vendor WHERE vendor_prov_code IN (SELECT area_code FROM area) LIMIT 10;
執行流程(傳統優化器):
SELECT area_code FROM area 獲取結果集SELECT * FROM vendor WHERE vendor_prov_code = 結果1; SELECT * FROM vendor WHERE vendor_prov_code = 結果2; ... SELECT * FROM vendor WHERE vendor_prov_code = 結果N;
| 版本 | IN 子查詢優化 | EXISTS 優化 | 建議 |
|---|---|---|---|
| 5.5 及之前 | 無優化,物化子查詢慢 | 相對穩定 | 優先用 EXISTS |
| 5.6 - 5.7 | 引入 semijoin、物化優化 | 保持優勢 | 視數據分布測試 |
| 8.0+ | 深度優化,常自動轉換為 JOIN | 同樣優秀 | 可互換,看可讀性 |
-- 外層 100 行,內層 100 萬行 SELECT * FROM small_table s WHERE EXISTS (SELECT 1 FROM large_table l WHERE l.fk = s.id);
EXISTS 只需 100 次索引查找,IN 需先掃描 100 萬行構建集合。
-- 外層 100 萬行,內層 10 行 SELECT * FROM large_table l WHERE l.category IN (SELECT id FROM small_category);
MySQL 5.6+ 會將小結果集物化到臨時表,再做高效 JOIN。
-- IN 含 NULL 時邏輯異常 SELECT * FROM t1 WHERE col IN (1, 2, NULL); -- 永遠不會匹配 NULL -- EXISTS 不受影響 SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.col = t1.col);
-- 替代 IN SELECT v.* FROM vendor v JOIN area a ON v.vendor_prov_code = a.area_code; -- 替代 EXISTS(語意更清晰) SELECT v.* FROM vendor v WHERE EXISTS (SELECT 1 FROM area a WHERE a.area_code = v.vendor_prov_code);
EXPLAIN ANALYZE SELECT * FROM vendor WHERE vendor_prov_code IN (SELECT area_code FROM area);
關注 actual time、rows、loops 等實際執行指標,而非估算值。
IS NOT NULL