1 准備數據
1.1 建表
- DROP TABLE IF EXISTS staff;
- CREATE TABLE IF NOT EXISTS staff (
- id INT PRIMARY KEY auto_increment,
- name ),
- age INT,
- pos ) COMMENT '職位',
- salary ,)
- );
1.2 插入數據
- , );
2 測試&Explain分析
2.1 有索引的情況下%的影響(提出問題)
2.1.1 建立索引
- CREATE INDEX idx_nameAgePos ON staff(name, age, pos);
2.1.2 測試&Explain分析
Case#1:兩邊都是%
- EXPLAIN SELECT * FROM staff WHERE name LIKE '%Alice%';
結果:type=all,全表掃描
Case#2:左邊是%
- EXPLAIN SELECT * FROM staff WHERE name LIKE '%Alice';
結果:type=all,全表掃描
Case#3:右邊是%
- EXPLAIN SELECT * FROM staff WHERE name LIKE 'Alice%';
結果:type=range,效果還可以。
對上面三個例子的總結:
- 都是 SELECT *
- %在左邊,即使有索引,也會失效
- 只有當%在右邊時,才會生效
但問題是,生產環境中,就是要支持模糊查詢(%在右邊是不夠的),一定要兩邊都是%來查詢,這可咋辦?
2.2 無索引情況下的查詢匯總
2.2.1 刪除索引
- DROP INDEX idx_nameAgePos ON staff;
2.2.2 測試&Explain分析(主要為了和 2.3.2 節做對比測試)
Case#4:查詢Id
- EXPLAIN SELECT id FROM staff WHERE name LIKE '%Alice%';
Case#5:查詢name
- EXPLAIN SELECT name FROM staff WHERE name LIKE '%Alice%';
Case#6:查詢age
- EXPLAIN SELECT age FROM staff WHERE name LIKE '%Alice%';
Case#7:查詢 id & name
- EXPLAIN SELECT id, name FROM staff WHERE name LIKE '%Alice%';
Case#8:查詢 name & age
- EXPLAIN SELECT name, age FROM staff WHERE name LIKE '%Alice%';
Case#9:查詢 id & name & age
- EXPLAIN SELECT id, name, age FROM staff WHERE name LIKE '%Alice%';
Case#10:查詢 id & name & age & salary (提示:salary 不在索引列中,后面會用上)
- EXPLAIN SELECT id, name, age, salary FROM staff WHERE name LIKE '%Alice%';
Case#11:查詢 *
- EXPLAIN SELECT * FROM staff WHERE name LIKE '%Alice%';
從 Case#4 到 Case#11 可以看出,在沒有索引的情況下,兩邊都使用 % 來查詢,不管想查詢哪個字段(包括查詢Id),全部都是全表掃描。
2.3 有索引情況下的查詢匯總
2.3.1 建立索引
- CREATE INDEX idx_nameAgePos ON staff(name, age, pos);
2.3.2 測試&Explain分析
IndexCase#4:查詢Id
- EXPLAIN SELECT id FROM staff WHERE name LIKE '%Alice%';
結果:使用上了索引(因為 name 有索引,同時查詢的 Id 是主鍵肯定也有索引)
IndexCase#5:查詢name
- EXPLAIN SELECT name FROM staff WHERE name LIKE '%Alice%';
結果:使用上了索引(因為查詢條件和查詢字段都是有索引的 name)
IndexCase#6:查詢age
- EXPLAIN SELECT age FROM staff WHERE name LIKE '%Alice%';
結果:使用上了索引(因為查詢條件的 name 以及查詢字段的 age 都有索引)
IndexCase#7:查詢 id & name
- EXPLAIN SELECT id, name FROM staff WHERE name LIKE '%Alice%';
結果:使用上了索引(因為查詢條件的 name 以及查詢字段的 id & name 都有索引)
IndexCase#8:查詢 name & age
- EXPLAIN SELECT name, age FROM staff WHERE name LIKE '%Alice%';
結果:使用上了索引(因為查詢條件的 name 以及查詢字段的 name & age 都有索引)
IndexCase#9:查詢 id & name & age
- EXPLAIN SELECT id, name, age FROM staff WHERE name LIKE '%Alice%';
結果:使用上了索引(因為查詢條件的 name 以及查詢字段的 id & name & age 都有索引)
IndexCase#10:查詢 id & name & age & salary (提示:salary 不在索引列中)
- EXPLAIN SELECT id, name, age, salary FROM staff WHERE name LIKE '%Alice%';
結果:沒有索引,type=all,全表掃描!(因為查詢字段中多了個 salary 而 salary 不在索引列中)
IndexCase#11:查詢 *
- EXPLAIN SELECT * FROM staff WHERE name LIKE '%Alice%';
結果:沒有索引,type=all,全表掃描!(因為 * 包含 salary 而 salary 不在索引列中)
通過 IndexCase#4 到 IndexCase#11 可以看出,當真的需要兩邊都使用%來模糊查詢時,只有當這個作為模糊查詢的條件字段(例子中的name)以及所想要查詢出來的數據字段(例子中的 id & name & age)都在索引列上時,才能真正使用索引,否則,索引失效全表掃描(比如多了一個 salary 字段)。我想,這應該就是 ‘覆蓋索引(索引覆蓋)’ 的本質吧。同時,這也能很好的證實 “盡量避免SELECT * 而是一一羅列出所需要查詢的字段” 的道理吧,因為,搞不好 SELECT * 就多了一個字段,就導致了全表掃描。
3 結論
LIKE以%開頭會導致索引失效;使用覆蓋索引解決之