造成索引失效的原因可能有很多种,今天我要记录的是隐式转换造成的索引失效。

一、什么是隐式转换

在MySQL查询中,当查询条件左右两侧类型不匹配的时候会发生隐式转换,可能导致查询无法使用索引。但并不是所有的类型转换都是隐式转换,不都是会造成索引失效,导致全表查询。

二、MySQL在官网描述了进行隐式类型转换的一些规则如下:

1、如果一个或两个参数都是NULL,比较的结果是NULL,除了NULL安全的<=>相等比较运算符。对于NULL <=> NULL,结果为true。不需要转换

2、如果比较操作中的两个参数都是字符串,则将它们作为字符串进行比较。

3、如果两个参数都是整数,则将它们作为整数进行比较。

4、如果不与数字进行比较,则将十六进制值视为二进制字符串

5、如果其中一个参数是十进制值,则比较取决于另一个参数。 如果另一个参数是十进制或整数值,则将参数与十进制值进行比较,如果另一个参数是浮点值,则将参数与浮点值进行比较

6、如果其中一个参数是TIMESTAMP或DATETIME列,另一个参数是常量,则在执行比较之前将常量转换为时间戳。

三、试验几种隐式转换场景

第一种情况:索引字段是varchar类型

 

SQL SERVER 隐性转换 mysql隐式转换的优化_字符串

 select * from gu where age=’30’;

因为等号左右边的字段类型一致,所以查询sql执行计划,使用了索引。

 

SQL SERVER 隐性转换 mysql隐式转换的优化_字段_02

  select * from gu where age=30;

 

SQL SERVER 隐性转换 mysql隐式转换的优化_SQL SERVER 隐性转换_03

因为等号两侧类型不一致,因此会发生隐式转换,cast(index_filed as signed),然后和30进行比较,MySQL无法使用索引只能进行全表扫描。

第二种情况:索引字段是int类型

 

SQL SERVER 隐性转换 mysql隐式转换的优化_SQL SERVER 隐性转换_04

 explain select * from user where weitht=38;

等号左侧字段类型是Int,右侧也是int类型,所以不会发生隐式转换。查询执行计划可以看到使用了索引。

 

SQL SERVER 隐性转换 mysql隐式转换的优化_SQL SERVER 隐性转换_05

 explain select * from user where weitht=’38’;

SQL SERVER 隐性转换 mysql隐式转换的优化_SQL SERVER 隐性转换_06

这次等号右侧是'38',注意带单引号哟,左侧的索引字段是int类型,因此也会发生隐式转换,但因为int类型的数字只有38能转化为'38',是唯一确定的。所以虽然需要隐式转换,但不影响使用索引,不会导致慢查询。

第三种情况:索引字段是datetime或timestamp类型

 

SQL SERVER 隐性转换 mysql隐式转换的优化_字段_07

参数一边是datetime或timestamp类型,另一边是字符串,字符串会被转为timestamp,但是从执行计划中可以看到,仍然使用了索引。

 

SQL SERVER 隐性转换 mysql隐式转换的优化_隐式转换_08

类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较

增加record字段,类型为decimal(6,2)

 

SQL SERVER 隐性转换 mysql隐式转换的优化_字符串_09

  explain select * from gu where record=1000.00;

等号左右边的类型一致,看执行计划,是使用了索引。

 

SQL SERVER 隐性转换 mysql隐式转换的优化_字段_10

 explain select * from gu where record =1000;

等号左右边的类型不一致,左边是decimal,右边是整数,查看执行计划,可以看到使用了索引。

 

SQL SERVER 隐性转换 mysql隐式转换的优化_字段_11

 explain select * from gu where record = ‘1000.00’;

等号右边为字符串类型,查看执行计划,可以看到也使用了索引

 

SQL SERVER 隐性转换 mysql隐式转换的优化_隐式转换_12

四、总结

1、避免发生隐式类型转换,隐式转换的类型主要有字段类型不一致、in参数包含多个类型、字符集类型或校对规则不一致等

2、隐式类型转换可能导致无法使用索引、查询结果不准确等,因此在使用时必须仔细甄别

数字类型的建议在字段定义时就定义为int或者bigint,表关联时关联字段必须保持类型、字符集、校对规则都一致

3、如果对sql语句有没有使用索引感到纠结,就使用命令explain来验证