当前位置: 首页 > 科技观察

MySQL数据类型隐式转换规则

时间:2023-03-12 01:31:07 科技观察

现象今天遇到慢查询,查询日志发现慢查询语句如下:select*fromconvert_testwhereareacode=0001andperiod>='20170511'andperiod<='20170511';convert_test表的结构如下:CREATETABLE`convert_test`(`id`bigint(20)unsignedNOTNULLDEFAULT_INCREMENT,`areacode`char(12)NOTNULLDEFAULT'',`period`int(6)unsignedNOTNULLDEFAULT0,`mid_price`int(10)unsignedNOTNULLDEFAULT0,`mid_changed`timeupLDEFAULT0floatNOTNate,`timestampNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,PRIMARYKEY(`id`),UNIQUEKEY`idx_areacode_period`(`areacode`,`period`))ENGINE=InnoDBDEFAULTCHARSET=utf8COMMENT='implicitconversiontesttable;表中数据超过42W。乍一看,很明显已经创建了唯一索引。正常情况下,上面的查询语句应该正好是***idx_areacode_period索引,不应该是慢查询。为了看这条语句是如何查询出来的,我们在测试库中进行说明:mysql>explainselect*fromconvert_testwhereareacode=0001andperiod>='20170511'andperiod<='20170511';结果如下:可以看到,这里没有使用索引。定义表时,areacode字段为string类型,查询时传入0001。这里0001被Mysql当做整数处理为1,Mysql检测到areacode字段的查询类型是整数,就会对全表进行扫描,将所有行的areacode转为整数,然后执行查询处理。找到原因,解决起来就很容易了。上述sql语句修改如下:mysql>explainselect*fromconvert_testwhereareacode='0001'andperiod>='20170511'andperiod<='20170511';结果如下:可以看到完全是***了idx_areacode_period这个索引。扩展上面的period定义的时候,是整型,但是query传入的是string类型,为什么要***索引呢?看看官方的隐式测试转换说明:当两个参数至少有一个为NULL时,比较的结果也为NULL。例外的是当使用<=>比较两个NULL时,会返回1。这两种情况都不需要做。类型转换的两个参数都是字符串,会按照字符串进行比较。如果不进行类型转换,则两个参数都是整数,将按整数进行比较。它将被视为二进制字符串。一个参数是TIMESTAMP或DATETIME,另一个参数是一个常量,常量会被转换成时间戳。一个参数是十进制类型。如果另一个参数是小数或整数,则整数将被转换为小数。为了比较,如果另一个参数是浮点数,则将小数转换为浮点数进行比较。在其他情况下,两个参数都将转换为浮点数以进行比较。因此,下面的SQL语句具有相同的效果:select*fromconvert_testwhereareacode=0001andperiod>='20170511'andperiod<='20170511';select*fromconvert_testwhereareacode=1andperiod>='20170511'andperiod<='20170511';select*fromconvert_testwhereacode=0001.0andperiod>='20170511'andperiod<='20170511';select*fromconvert_testwhereareacode=1.0andperiod>='20170511'andperiod<='20170511';mysql在查询的时候会将areacode转成浮点型进行比较