子查詢雖然語法直觀,但在 MySQL 中長期存在執行計劃缺陷,導致大數據量下性能急劇下降。本文深入分析子查詢與關聯查詢的性能差異,並提供索引優化、改寫為 JOIN 等實戰方案。
SELECT a.name, (SELECT b.name FROM b WHERE b.id=a.id) FROM a WHERE a.name LIKE 'a%'SELECT a.name, b.name FROM a, b WHERE a.id=b.id AND a.name LIKE 'a%'MySQL 預設對子查詢採用全表掃描執行計劃,總 I/O 次數 ≈ 主表行數 × 子表行數。當雙表均超過 1 萬行,查詢耗時易超過 10 秒,且隨數據增長呈指數級惡化。官方雖早已確認該問題,但穩定版至今未根治。
在子表關聯字段(如 b.id)創建索引,可將全表掃描轉為索引查找,大幅降低 I/O。
將子查詢轉換為等值連接,利用 MySQL 成熟的 JOIN 優化器:
-- 原子查詢
SELECT a.name FROM a WHERE a.id IN (SELECT b.a_id FROM b WHERE b.status=1);
-- 改寫為 JOIN
SELECT a.name FROM a INNER JOIN b ON a.id = b.a_id WHERE b.status = 1;
當子查詢返回結果集較大時,EXISTS 往往比 IN 更高效,因為它只需判斷是否存在匹配行,而非獲取所有值。
EXPLAIN 對比執行計劃,關注 type、rows、Extra 列Handler_read_key、Handler_read_rnd_next 狀態變量評估索引命中率核心原則:能用 JOIN 解決的場景,堅決不用子查詢。MySQL 8.0+ 對子查詢優化有所改善,但生產環境仍建議保守改寫。