5.8 明明有索引,就是不走,怎么办?4种



介绍索引失效的几种情况。

一、索引失效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.解决:
因为这种时间函数就是很难优化:

这里只能通过between一个一个的叠加:

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中,如果对索引的字段,进行函数操作,会导致索引失效。

原因是索引的排序规则不再起作用(破坏了索引键线性递增的规则),有可能是显式,也有可能是隐式,比如以下三种。

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 5.8 明明有索引,就是不走,怎么办?4种


Stop chasing money, and start chasing the solutions to the problem.