當你使用 EXPLAIN 分析 SQL 執行計劃時,若發現 select_type 欄位出現 DEPENDENT SUBQUERY,這就是 MySQL 子查詢效能低落的典型訊號。這代表子查詢的執行依賴於外層查詢的結果,導致子查詢需重複執行多次,嚴重拖慢查詢速度。
假設有以下查詢語句:
SELECT gid, COUNT(id) AS count
FROM shop_goods g1
WHERE status = 0
AND gid IN (
SELECT gid
FROM shop_goods g2
WHERE sid IN (1519066,1466114,1466110,1466102,1466071,1453929)
)
GROUP BY gid;
執行 EXPLAIN 後發現 select_type 為 DEPENDENT SUBQUERY,意味著:
換句話說,MySQL 會先根據外層查詢 SELECT gid, COUNT(id) FROM shop_goods WHERE status=0 GROUP BY gid 產生一個龐大的結果集(假設 85 萬筆),然後對結果集中的每一筆記錄,都執行一次子查詢。即使有索引支援,85 萬次子查詢執行也極其緩慢。
先執行子查詢取得 gid 清單,再用應用層或臨時表帶入主查詢,避免關聯子查詢的逐行執行模式。
SELECT g1.gid, COUNT(1)
FROM shop_goods g1,
(SELECT gid FROM shop_goods WHERE sid IN (1519066,1466114,1466110,1466102,1466071,1453929)) g2
WHERE g1.status = 0
AND g1.gid = g2.gid
GROUP BY g1.gid;
再次 EXPLAIN 會出現 DERIVED 關鍵字,表示 MySQL 會先遞歸執行子查詢,將結果存入臨時表,再進行 JOIN 操作。這樣子查詢只執行一次,效能大幅提升。
根據《高效能 MySQL》第 4.4.1 節所述,MySQL 內部會將 IN 子查詢改寫為 EXISTS 相關子查詢:
-- 原始寫法
SELECT * FROM test WHERE tid IN (SELECT fk_tid FROM sub_test WHERE gid=10);
-- MySQL 實際改寫執行邏輯
SELECT * FROM test WHERE EXISTS (
SELECT * FROM sub_test WHERE gid=10 AND sub_test.fk_tid = test.tid
);
這導致 MySQL 掃描外層表 test 的所有資料,每筆都帶入子查詢關聯。若外層表資料量大,效能極差。因此,盡量避免使用關聯子查詢,優先考慮 JOIN 或衍生表改寫。
DEPENDENT SUBQUERY 即代表效能隱憂JOIN + 衍生表(DERIVED)EXPLAIN 驗證優化成果