MySQL 8.0.33
CREATE TABLE people (
id INT AUTO_INCREMENT PRIMARY KEY,
zipcode INT,
firstname VARCHAR(50),
lastname VARCHAR(50),
addr VARCHAR(100),
city VARCHAR(50),
state CHAR(2),
KEY idx_zip_fn_ln (zipcode, firstname, lastname)
) ENGINE=InnoDB;
INSERT INTO people (zipcode, firstname, lastname, addr, city, state)
SELECT
FLOOR(10000 + RAND()*90000),
ELT(FLOOR(1+RAND()*10), 'John','Jane','Bob','Alice','Tom','Mary','Mike','Lisa','David','Sarah'),
ELT(FLOOR(1+RAND()*20), 'Smith','Johnson','Williams','Brown','Jones','Garcia','Miller','Davis','Wilson','Anderson','Taylor','Thomas','Moore','Jackson','Martin','Lee','Thompson','White','Harris'),
CONCAT('Addr ', FLOOR(1+RAND()*10000)),
ELT(FLOOR(1+RAND()*5), 'NY','LA','Chicago','Houston','Phoenix'),
ELT(FLOOR(1+RAND()*3), 'NY','CA','IL')
FROM (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) t1
CROSS JOIN (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) t2
CROSS JOIN (SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 3 UNION ALL SELECT 4) t3; -- ~256 rows
EXPLAIN SELECT * FROM (SELECT id FROM people) a;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+----------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+----------+
| 1 | PRIMARY | | system | NULL | NULL | NULL | NULL | 1 | NULL |
| 2 | DERIVED | people | index | NULL | idx_zip_fn_ln | 158 | NULL | 256 | Using index |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+----------+
id=2 (DERIVED) 先執行,掃描覆蓋索引產生衍生表;id=1 (PRIMARY) 讀取衍生表 (system: 單行)。| 類型 | 含義 | 典型場景 |
|---|---|---|
| SIMPLE | 無 UNION/子查詢 | SELECT * FROM people |
| PRIMARY | 最外層/首個 SELECT | 嵌套查詢外層、UNION 首個 |
| UNION | UNION 後續 SELECT | SELECT 1 UNION SELECT 2 第二行 |
| DEPENDENT UNION | UNION 中依賴外部查詢 | SELECT * FROM t1 WHERE id IN (SELECT id FROM t2 WHERE t2.x=t1.x) UNION 部分 |
| SUBQUERY | 子查詢首個 SELECT | SELECT * FROM t1 WHERE id = (SELECT MAX(id) FROM t2) |
| DEPENDENT SUBQUERY | **關聯子查詢** (依賴外層列) | SELECT * FROM t1 WHERE id IN (SELECT t2.id FROM t2 WHERE t2.val=t1.val) **性能殺手** |
| DERIVED | FROM 子句衍生表 | SELECT * FROM (SELECT * FROM t1) dt |
| UNION RESULT | UNION 合併結果 | 臨時表合併 |
<derivedN>:id=N 的衍生表結果 (無索引,慎用大結果集)。<unionM,N>:UNION 合併的臨時表。由優到劣:
EXPLAIN SELECT * FROM people WHERE id=1;EXPLAIN SELECT * FROM people p JOIN people_car pc ON p.id=pc.people_id WHERE pc.people_id IS UNIQUE;EXPLAIN SELECT * FROM people WHERE zipcode=10000; (idx_zip_fn_ln 最左列)IS NULL 子查詢)。Using intersect/union/sort_union),**通常不如聯合索引**。>, >=, <, <=, BETWEEN, IN, LIKE 'prefix%')。**注意**:IN (1,2,3) 視為多個等值範圍,**可用聯合索引後綴列**;id > 10 範圍查詢**不可用聯合索引後綴列**。possible_keys:優化器候選索引集合。key:實際選用索引。NULL = 無索引可用/全掃。**可用 FORCE INDEX(idx) / IGNORE INDEX(idx) 干預**。key_len:使用的索引字節長度。**推斷聯合索引用了幾列**:idx_zip_fn_ln (zipcode INT, firstname VARCHAR(50), lastname VARCHAR(50)) → 完整長度 4 + 152 + 152 = 308。若 key_len=4 僅用 zipcode;key_len=156 用了 zipcode+firstname。const:常量。func:函數/表達式結果 (如關聯子查詢)。rows:優化器預估掃描行數 (基於統計信息,**可能嚴重偏差**),ANALYZE TABLE 更新統計。filtered (5.7+):表條件過濾後剩餘百分比。**rows * filtered% ≈ 實際參與 Join 的行數**,用於評估驅動表大小。key_len 幾列?