条件字段函数操作

假如我们现在有如下需求,我们要统计一张表2021年和2022年8月份的记录,那么SQL语句可以这么写:

1
select column_1, column_2 from table_1 where month(record_time) = 7;

虽然数据库表在record_time上加了索引(结构图如下),但是这个sql还是会执行的特别慢。

image-20230412151341061

因为在用month函数对record_time做计算后,得到的数字是记录的月份,而索引的值却并不是一个单独的数字,所以会导致它走全表扫描。

也就是说,对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。

1
2
3
4
5
// 无法使用id这个索引
select * from table_1 where id + 1 = 1000

// 可以使用
select * from table_1 where id = 1000 - 1

隐式类型转换

现在有如下例子:

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上却走了全表扫描。

这个语句的执行流程是,从驱动表中找到一行数据,然后去被驱动表查找对值一样的数据组成结果集,过程如下:

image-20230412153535466

而这里走全表扫描的原因是发生了类型的转换。这里两个表的字段一个类型是utf8,另一个是utf8mb4,导致实际执行的sql相当于:

1
select * from trade_detail  where CONVERT(traideid USING utf8mb4)=$L2.tradeid.value; 

CONVERT()函数,在这里的意思是把输入的字符串转成utf8mb4字符集。

而这里导致全表扫描的原因也是因为对索引字段加了函数操作。

参考

《MySQL45讲》