介绍索引失效的几种情况。
一、索引失效1
1.1 显式:查询语句包含函数操作,where film_id+1=100;
1.试验
在数据表film中,字段film_id就是主键索引:
explain select * from film where film_id+1=100;但是,没有走索引:
2.理论
上面的”+1“,是对索引字段,进行了函数操作。这会破坏索引键线性递增的规则。
也就是说:主键索引是针对字段film_id的索引,不是针对字段film_id+1的索引。
3.解决
上面的错误,是简单的,因为很明显,大家都会避免。解决如下:
explain select * from film where film_id=99;但是,很多情况下,索引失效的问题会严峻的多,因为隐蔽。
二、索引失效2:时间函数
2.1 隐蔽:查询语句包含时间函数,WHERE month(rental_date)=5;
1.试验
在数据表rental中:查询语句包含时间函数,查询条件是只能5月份
SELECT * FROM `rental` WHERE month(rental_date)=5;先查一下该表有哪些索引:
show index from rental;发现:字段rental_date是联合索引,且是第一顺位:
再看一下查询语句的执行计划:
没有走任何索引,全表扫描:16008行
2.理论
因为函数操作,会破坏索引原来的排序规则:
即索引字段,被函数计算过后,没法使用索引:
3.解决:
因为这种时间函数就是很难优化:
SELECT * FROM `rental` WHERE rental_date BETWEEN '2005-5-1' AND '2005-6-1' OR rental_date BETWEEN '2006-5-1' AND '2006-6-1';走了索引,只1157行:
三、索引失效3:字符串与数字比较
有可能有隐式转换。
3.1 准备:数据类型不一致时
- 建表语句
CREATE TABLE `t1` (
`f1` varchar(32) NOT NULL,
`f2` int NOT NULL,
KEY `idx_f1` (`f1`),
KEY `idx_f2` (`f2`)
) ENGINE= InnoDB DEFAULT CHARSET=utf8mb4;INSERT INTO `sakila`.`t1`(`f1`, `f2`) VALUES ('4', 6);
INSERT INTO `sakila`.`t1`(`f1`, `f2`) VALUES ('6', 3);
INSERT INTO `sakila`.`t1`(`f1`, `f2`) VALUES ('7', 1);完成建表:
3.2 理论:会自动强转(隐式),优先将VARCHAR字符串转换成数字
问题:
f1是VARCHAR字符串类型的,而6没有加任何符号,说明是数字。
当出现以上两种格式时,会自动强转,优先将VARCHAR字符串转换成数字,再判断。
这就很会出现索引失效:
select * from t1 where f1=6;解决:
确保数据类型一致:
3.3 作业
如果f2是int整型,而与字符串'6'进行比较呢?
select * from t1 where f2='6';有区别吗?
四、索引失效4:隐式字符编码转换
4.1 准备:两张表字符集编码不一致时
新建t2表,并执行以下语句:
CREATE TABLE `t2` (
`f1` varchar(32) NOT NULL,
`f2` int NOT NULL,
KEY `idx_f1` (`f1`),
KEY `idx_f2` (`f2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `sakila`.`t2`(`f1`, `f2`) VALUES ('4', 5);
INSERT INTO `sakila`.`t2`(`f1`, `f2`) VALUES ('2', 4);
INSERT INTO `sakila`.`t2`(`f1`, `f2`) VALUES ('2', 4);建表完成:
注意:上述两张表的字符编码不一样:
- t1:utf8mb4------------------- 覆盖范围更大、更通用,更高级
- t2:utf8
4.2 试验:不走索引的问题
1.
SQL语句,最好从后往前看:
select t2.* from t1,t2 where t1.f1=t2.f1 and t1.f2=6;结果:
表t1能走索引,表t2走不了索引:
4.3 理论
1.原因
即默认的自动进行了字符集转换的函数操作:
2.解决:
你表t1不是已经执行了嘛,拿你就先主动的转成接下来的t2一样的字符集格式吧,这样t2表就不用函数操作,就能直接走索引了:
select t2.* from t1, t2 where CONVERT(t1.f1 USING utf8) = t2.f1 and t1.f2=6; 试验:都走索引了
五、小结
MySQL中,如果对索引的字段,进行函数操作,会导致索引失效。
原因是索引的排序规则不再起作用(破坏了索引键线性递增的规则),有可能是显式,也有可能是隐式,比如以下三种。






















Comments | NOTHING