EXISTS/SUBQUERY 往往比外連接慢,用 INNER JOIN 改寫。SELECT * FROM A WHERE EXISTS (SELECT 1 FROM B WHERE A.id=B.id) → SELECT * FROM A INNER JOIN B USING(id)。LEFT JOIN ... ON 條件 WHERE 條件:先執行 ON 過濾右表,再 WHERE 過濾連接結果。ON 不影響左表行數,WHERE 影響最終結果。
編寫 JOIN 時,結果集小的表放驅動位置(LEFT JOIN 左、RIGHT JOIN 右),並盡量拆分多表連接為多條簡單查詢,減少鎖競爭。
name LIKE 'abc%' 用索引,LIKE '%abc' 全表掃描。INDEX(a,b,c) 等價於 (a,b,c)、(a,b)、(a) 三個索引(最佳左前綴)。NOT NULL DEFAULT ''。INDEX(name(10))),節省空間提速。LIMIT 10000000,10 極慢(需掃描前 1000 萬行)。WHERE id >= (SELECT id FROM t LIMIT 10000000,1) LIMIT 10(需 id 索)。WHERE id BETWEEN 10000000 AND 10000010(連續 ID 範圍)。只查需要的列,減少磁盤 I/O、網絡傳輸、內存佔用。
全表掃描+排序極慢。替代:應用層隨機 ID、或 WHERE id >= (SELECT FLOOR(RAND()*MAX(id)) FROM t) LIMIT 1。
SELECT * FROM t WHERE name LIKE '%xxx' LIMIT 1 命中即停止掃描。
排序消耗 CPU,緩存命中率高時可接受,否則優化索引或應用層排序。
MySQL 優化器處理 OR 差,改用 UNION ALL(無去重)或 UNION(去重)。
無去重需求時 UNION ALL 避免排序去重開銷。
WHERE 列與參數類型不一致導致列轉換 → 索引失效。轉換應在參數端:WHERE col = CAST(? AS INT)。
WHERE YEAR(dt) < 2023 → 全表掃描。改為 WHERE dt < '2023-01-01'。
不走索引。NOT IN → NOT EXISTS 或 LEFT JOIN ... WHERE IS NULL。id != 3 → id > 3 OR id < 3。
INSERT INTO t(a,b) VALUES (1,2),(3,4),(5,6) 減少交互。大批量用 LOAD DATA INFILE 或存儲過程。