EXPLAIN 是 SQL 優化的「X 光機」。本文聚焦 select_type 欄位的八大類型,配合實戰案例與圖示,幫你一眼看穿查詢結構,精準優化。
EXPLAIN SELECT sid, filename, filetype FROM (SELECT * FROM by_disk_file WHERE uid = 830814) AS a;
| 類型 | 含義 | 典型觸發場景 | 優化提示 |
|---|---|---|---|
| SIMPLE | 無 UNION、子查詢的單純 SELECT | SELECT * FROM t WHERE id=1 |
專注索引、type、rows、Extra |
| PRIMARY | 最外層查詢(包含子查詢/衍生表的主查詢) | SELECT * FROM (SELECT * FROM t) a |
外層通常無 WHERE,關鍵看內層 |
| UNION | UNION 第 2 個及之後的 SELECT | SELECT * FROM t1 UNION SELECT * FROM t2 |
各分支獨立優化;注意 UNION ALL 避免去重排序 |
| DEPENDENT UNION | UNION 後續 SELECT 依賴外層查詢 | SELECT * FROM t1 WHERE id IN (SELECT id FROM t2 UNION SELECT id FROM t3) |
嘗試改寫為 JOIN 或拆分 |
| UNION RESULT | UNION 產生的臨時結果表 | 多重 UNION 時出現 | 通常 type=ALL,無索引,關注 rows |
| SUBQUERY | 子查詢第一個 SELECT(非相關) | SELECT * FROM t1 WHERE id = (SELECT uid FROM t2 WHERE name='a') |
常可改寫為 JOIN,效能更穩 |
| DEPENDENT SUBQUERY | 相關子查詢(依賴外層列) | SELECT * FROM t1 WHERE id IN (SELECT sid FROM t2 WHERE t2.uid = t1.uid) |
效能殺手,優先改寫為 JOIN/EXISTS |
| DERIVED | FROM 子句中的子查詢(衍生表) | SELECT * FROM (SELECT * FROM t WHERE uid=1) a |
MySQL 5.6+ 可合併衍生表;8.0+ 支援 CTE 更優雅 |
-- EXPLAIN 輸出 id 遞減,執行順序由大到小 id | select_type | table 3 | UNION RESULT |2 | UNION | t2 1 | PRIMARY | t1 -- 實際執行:t1 → t2 → 合併去重 → 返回
-- 相關子查詢(DEPENDENT SUBQUERY) EXPLAIN SELECT * FROM by_disk_file WHERE sid IN (SELECT sid FROM by_disk_file WHERE uid = 830814); -- 改寫為 JOIN(SIMPLE + SIMPLE) EXPLAIN SELECT a.* FROM by_disk_file a JOIN by_disk_file b ON a.sid = b.sid WHERE b.uid = 830814;
結論:JOIN 穩定走索引,子查詢易退化全表掃描,尤其 IN (SELECT ...)。
UNION ALLWITH filtered AS ( SELECT * FROM by_disk_file WHERE uid = 830814 ) SELECT sid, filename, filetype FROM filtered;
EXPLAIN 顯示 select_type=SIMPLE 或 PRIMARY,無 DERIVEDEXPLAIN 的 select_type 告訴你「查詢長什麼樣」。記住:SIMPLE 好、PRIMARY 看內層、UNION 注意去重、SUBQUERY/DEPENDENT SUBQUERY 能改 JOIN 就改、DERIVED 考慮 CTE。養成「寫完 SQL 必跑 EXPLAIN」習慣,優化才會有的放矢。