MySQL索引失效的情况
条件字段函数操作
假如我们现在有如下需求,我们要统计一张表2021年和2022年8月份的记录,那么SQL语句可以这么写:
1 | select column_1, column_2 from table_1 where month(record_time) = 7; |
虽然数据库表在record_time上加了索引(结构图如下),但是这个sql还是会执行的特别慢。

因为在用month函数对record_time做计算后,得到的数字是记录的月份,而索引的值却并不是一个单独的数字,所以会导致它走全表扫描。
也就是说,对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。
1 | // 无法使用id这个索引 |
隐式类型转换
现在有如下例子:
1 | select * from tradelog where id = 110717; |
其中,id是一个varchar类型的值,虽然它上面有索引,但还是走了全表扫描。这里就是因为id是varchar,而输入的值是数字,导致类型转换。
而发生转换要走全表索引的原因是转换相当于一个函数调用,上面语句相当于:
1 | select * from tradelog where CAST(id AS signed int) = 110717; |
而对索引字段使用函数会导致无法使用索引。
隐式字符编码转换
假如有如下语句
1 | select d.* from tradelog l, trade_detail d where d.tradeid = l.tradeid and l.id = 2 |
这是一个简单的连表操作,其中tradelog为驱动表,trade_detail为被驱动表,tradeid为关联字段。
但是使用explain却发现在tradelog上使用了索引,但是在trade_detail上却走了全表扫描。
这个语句的执行流程是,从驱动表中找到一行数据,然后去被驱动表查找对值一样的数据组成结果集,过程如下:

而这里走全表扫描的原因是发生了类型的转换。这里两个表的字段一个类型是utf8,另一个是utf8mb4,导致实际执行的sql相当于:
1 | select * from trade_detail where CONVERT(traideid USING utf8mb4)=$L2.tradeid.value; |
CONVERT()函数,在这里的意思是把输入的字符串转成utf8mb4字符集。
而这里导致全表扫描的原因也是因为对索引字段加了函数操作。
参考
《MySQL45讲》