造成索引失效的原因可能有很多种,今天我要记录的是隐式转换造成的索引失效。
一、什么是隐式转换
在MySQL查询中,当查询条件左右两侧类型不匹配的时候会发生隐式转换,可能导致查询无法使用索引。但并不是所有的类型转换都是隐式转换,不都是会造成索引失效,导致全表查询。
二、MySQL在官网描述了进行隐式类型转换的一些规则如下:
1、如果一个或两个参数都是NULL,比较的结果是NULL,除了NULL安全的<=>相等比较运算符。对于NULL <=> NULL,结果为true。不需要转换
2、如果比较操作中的两个参数都是字符串,则将它们作为字符串进行比较。
3、如果两个参数都是整数,则将它们作为整数进行比较。
4、如果不与数字进行比较,则将十六进制值视为二进制字符串
5、如果其中一个参数是十进制值,则比较取决于另一个参数。 如果另一个参数是十进制或整数值,则将参数与十进制值进行比较,如果另一个参数是浮点值,则将参数与浮点值进行比较
6、如果其中一个参数是TIMESTAMP或DATETIME列,另一个参数是常量,则在执行比较之前将常量转换为时间戳。
三、试验几种隐式转换场景
第一种情况:索引字段是varchar类型
select * from gu where age=’30’;
因为等号左右边的字段类型一致,所以查询sql执行计划,使用了索引。
select * from gu where age=30;
因为等号两侧类型不一致,因此会发生隐式转换,cast(index_filed as signed),然后和30进行比较,MySQL无法使用索引只能进行全表扫描。
第二种情况:索引字段是int类型
explain select * from user where weitht=38;
等号左侧字段类型是Int,右侧也是int类型,所以不会发生隐式转换。查询执行计划可以看到使用了索引。
explain select * from user where weitht=’38’;
这次等号右侧是'38',注意带单引号哟,左侧的索引字段是int类型,因此也会发生隐式转换,但因为int类型的数字只有38能转化为'38',是唯一确定的。所以虽然需要隐式转换,但不影响使用索引,不会导致慢查询。
第三种情况:索引字段是datetime或timestamp类型
参数一边是datetime或timestamp类型,另一边是字符串,字符串会被转为timestamp,但是从执行计划中可以看到,仍然使用了索引。
类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
增加record字段,类型为decimal(6,2)
explain select * from gu where record=1000.00;
等号左右边的类型一致,看执行计划,是使用了索引。
explain select * from gu where record =1000;
等号左右边的类型不一致,左边是decimal,右边是整数,查看执行计划,可以看到使用了索引。
explain select * from gu where record = ‘1000.00’;
等号右边为字符串类型,查看执行计划,可以看到也使用了索引
四、总结
1、避免发生隐式类型转换,隐式转换的类型主要有字段类型不一致、in参数包含多个类型、字符集类型或校对规则不一致等
2、隐式类型转换可能导致无法使用索引、查询结果不准确等,因此在使用时必须仔细甄别
数字类型的建议在字段定义时就定义为int或者bigint,表关联时关联字段必须保持类型、字符集、校对规则都一致
3、如果对sql语句有没有使用索引感到纠结,就使用命令explain来验证