開發初期資料量小,SQL 寫法優劣不明顯;上線後資料增長,劣質 SQL 與優質 SQL 差距可達百倍。優化核心在於讓優化器用上索引,避免全表掃描。
-- 無法用索引
SELECT * FROM users WHERE email IS NULL;
-- 改法:預設值 + 索引
ALTER TABLE users MODIFY email VARCHAR(100) NOT NULL DEFAULT '';
CREATE INDEX idx_email ON users(email);
SELECT * FROM users WHERE email = '';
-- 索引失效
SELECT * FROM orders WHERE YEAR(created_at) = 2024;
SELECT * FROM users WHERE CONCAT(first_name, last_name) = 'JohnDoe';
-- 改法:範圍查詢 / 拆分欄位
SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
SELECT * FROM users WHERE first_name = 'John' AND last_name = 'Doe';
-- varchar 欄位對比數字,索引失效
SELECT * FROM users WHERE phone = 13800138000;
-- 改法:統一型別
SELECT * FROM users WHERE phone = '13800138000';
-- 全表掃描
SELECT * FROM products WHERE name LIKE '%phone%';
-- 改法:全文索引或前綴匹配
ALTER TABLE products ADD FULLTEXT(name);
SELECT * FROM products WHERE MATCH(name) AGAINST('phone');
-- 除非每個 OR 條件欄位都有索引,否則全掃
SELECT * FROM orders WHERE user_id = 1 OR order_no = 'A123';
-- 改法:UNION ALL 分開查詢再合併
SELECT * FROM orders WHERE user_id = 1
UNION ALL
SELECT * FROM orders WHERE order_no = 'A123';
(a,b,c) 支援 a、a,b、a,b,c 查詢;SELECT a,b FROM t WHERE c=1 配合索引 (c,a,b) 避免回表;EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 1;
重點欄位:
type:ALL(全掃) → index → range → ref → eq_ref → const → system(由差到好);key:實際使用的索引;rows:預估掃描行數;Extra:Using where、Using index(覆蓋)、Using temporary、Using filesort(需優化)。LIMIT 100000, 20 → 子查詢 WHERE id > (SELECT id FROM t ORDER BY id LIMIT 100000, 1) LIMIT 20;COUNT(*) 無條件用 MyISAM 直接讀統計資訊;InnoDB 需掃描,改用快取或觸發器維護計數表;1000 行/批,避免長鎖表與 Binlog 膨脹;