前提:數據准備
drop table if exists t1; /* 如果表t1存在則刪除表t1 */
CREATE TABLE `t1` ( /* 創建表t1 */
`id` int(11) NOT NULL AUTO_INCREMENT,
`a` varchar(20) DEFAULT NULL,
`b` int(20) DEFAULT NULL,
`c` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_a` (`a`) USING BTREE,
KEY `idx_b` (`b`) USING BTREE,
KEY `idx_c` (`c`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
drop procedure if exists insert_t1; /* 如果存在存儲過程insert_t1,則刪除 */
delimiter ;;
create procedure insert_t1() /* 創建存儲過程insert_t1 */
begin
declare i int; /* 聲明變量i */
set i=1; /* 設置i的初始值為1 */
while(i<=10000)do /* 對滿足i<=10000的值進行while循環 */
insert into t1(a,b) values(i,i); /* 寫入表t1中a、b兩個字段,值都為i當前的值 */
set i=i+1; /* 將i加1 */
end while;
end;;
delimiter ;
call insert_t1(); /* 運行存儲過程insert_t1 */
update t1 set c = '2019-05-22 00:00:00'; /* 更新表t1的c字段,值都為'2019-05-22 00:00:00' */
update t1 set c = '2019-05-21 00:00:00' where id=10000; /* 將id為10000的行的c字段改為與其它行都不一樣的數據,以便后面實驗使用 */
1.要查詢測試表 t1 單獨某一天的所有數據
explain select * from t1 where date(c) ='2019-05-21';
查看圖中的執行計划,type 為 ALL,key 字段結果為 NULL,因此知道該 SQL 是沒走索引的全表掃描
原因:對條件字段做函數操作走不了索引
2.隱式轉換
概念:當操作符與不同類型的操作對象一起使用時,就會發生類型轉換以使操作兼容。某些轉換是隱式的
explain select * from t1 where a=1000;
未走索引原因:
a 字段類型是 varchar(20),而語句中 a 字段條件值沒加單引號,導致 MySQL 內部會先把a轉換成int型,再去做判斷,相當於實際執行的 SQL 語句如下:
select * from t1 where cast(a as signed int) =1000;
因此又回到上面說的:對索引字段做函數操作時,優化器會放棄使用索引
優化后:
explain select * from t1 where a='1000';
隱式轉換導致查詢慢的情況在工作中遇到過幾次,有時字段名對開發寫SQL產生了影響,比如曾經遇到過字段名是user_num,而實際字段類型是char,但是開發在寫SQL時誤認為是int型,導致漏寫單引號而發生隱式轉換。
所以建議在寫SQL時,先看字段類型,然后根據字段類型寫SQL
3.模糊查詢(通配符在前面不走索引)
很多時候我們想根據某個字段的某幾個關鍵字查詢數據,比如會有如下 SQL:
優化過的:
缺點:結果會不准確
如果條件只知道中間的值,需要模糊查詢去查,那就建議使用ElasticSearch或其它搜索服務器
4.范圍查詢:
explain select * from t1 where b>=1 and b <=2000;
結果:不走索引
發現並不能走b字段的索引。
原因:優化器會根據檢索比例、表大小、I/O塊大小等進行評估是否使用索引。比如單次查詢的數據量過大,優化器將不走索引。
優化范圍查詢(分批查詢)
explain select * from t1 where b>=1 and b <=1000;
實際這種范圍查詢而導致使用不了索引的場景經常出現,比如按照時間段抽取全量數據,每條SQL抽取一個月的;或者某張業務表歷史數據的刪除。遇到此類操作時,應該在執行之前對SQL做explain分析,確定能走索引,再進行操作,否則不但可能導致操作緩慢,在做更新或者刪除時,甚至會導致表所有記錄鎖住,十分危險。
5.計算操作:
explain select * from t1 where b-1 =1000;
原因:對索引字段做運算將使用不了索引。
計算操作的 SQL 優化
explain select * from t1 where b =1000 + 1;
一般需要對條件字段做計算時,建議通過程序代碼實現,而不是通過MySQL實現。如果在MySQL中計算的情況避免不了,那必須把計算放在等號后面
總結以上結果:
- 應該避免隱式轉換
- like查詢不能以%開頭
- 范圍查詢時,包含的數據比例不能太大
- 不建議對條件字段做運算及函數操作