在MySQL数据库中,varchar和int是两种常用的数据类型。varchar用于存储可变长度的字符数据,而int则用于存储整数值。在实际应用中,我们可能会遇到需要在这两种数据类型之间进行转换的情况。MySQL提供了隐式转换机制,可以在某些情况下自动将数据类型进行转换,但同时也存在一些需要注意的细节。
现在来看一些常见的情况下varchar和INTEGER之间的隐式转换。当我们在一个操作中将varchar类型的列与int类型的列进行比较时,MySQL会尝试将varchar列的值转换为整数值,然后进行比较。例如,我们有一个名为"age"的varchar列和一个名为"age_limit"的int列,我们可以执行以下查询:
SELECT * FROM user WHERE age < age_limit;
在这个查询中,MySQL会将"age"列的值隐式地转换为整数值,然后与"age_limit"列的值进行比较。这种隐式转换可以方便地进行数据比较,但需要注意的是,如果"age"列的值无法转换为整数,或者不符合整数的范围,可能会导致错误的结果。
1 SELECT * FROM test_demo WHERE num_int = 10;
2 SELECT * FROM test_demo WHERE num_int = '10';
3 SELECT * FROM test_demo WHERE num_str = 10;
4 SELECT * FROM test_demo WHERE num_str = '10';
在这个查询中,1和2两条SQL查询语句的查询字段是int类型,3和4两条SQL查询语句的查询字段是varchar类型。以上四条查询语句的查询字段虽然相同,但是WHERE条件后一个是数字,一个是字符串。在千万级数据量下执行四条语句,1,2和4的性能基本没有差别,但是3的查询效率却很差。进一步,在执行以上语句,查看他们的执行计划信息,发现语句3没有用上索引,即执行了全表扫描,所以性能才会那么差。那么为什么会触发全表扫描呢?
查阅 MySQL 相关文档发现是隐式转换造成的。MySQL 8.0 的官方文档:https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html,
以下规则描述了比较操作如何进行转换:
1.如果一个或两个参数是NULL,则比较的结果是NULL,但NULL-safe <=> 相等比较运算符除外。对于NULL <=> NULL,结果为真。无需转换。
2.如果比较操作中的两个参数都是字符串,则将它们作为字符串进行比较。
3.如果两个参数都是整数,则将它们作为整数进行比较。
4.如果不与数字比较,十六进制值将被视为二进制字符串。
5.如果其中一个参数是 a TIMESTAMP或 DATETIME列,而另一个参数是常量,则在执行比较之前将常量转换为时间戳。这样做是为了对 ODBC 更友好。这不适用于 的参数 IN()。为了安全起见,在进行比较时,请始终使用完整的日期时间、日期或时间字符串。例如,要在使用 BETWEEN日期或时间值时获得最佳结果,请使用CAST()将值显式转换为所需的数据类型。
6.来自一个或多个表的单行子查询不被视为常量。例如,如果子查询返回要与值进行比较的整数DATETIME ,则比较将作为两个整数进行。整数不会转换为时间值。要将操作数作为 DATETIME值进行比较,请使用 CAST()将子查询值显式转换为DATETIME.
7.如果其中一个参数是十进制值,则比较取决于另一个参数。如果另一个参数是十进制或整数值,则将参数作为十进制值进行比较,如果另一个参数是浮点值,则将其作为浮点值进行比较。
8.在所有其他情况下,参数将作为浮点(双精度)数字进行比较。例如,字符串和数字操作数的比较是作为浮点数的比较进行的。
按照上述规则的最后一条,我们的查询SQL中,字符串与整数的比较会被转换成两个浮点数比较,左边是字符串类型 "1" 转换成浮点数为1.0,右边 INT类型的 1 转换成浮点数 1.0.
根据官方文档的描述,我们的第 2、3 两条 SQL 都发生了隐式转换,第 2 条 SQL 的查询条件num_int = '10',左边是int类型右边是字符串,第 3 条 SQL 相反,那么根据官方转换规则第 7 条,左右两边都会转换为浮点数再进行比较。在int类型的数据转换为字符串时,进一步查阅相关资料,发现存在以下规则:
1.不以数字开头的字符串都将转换为0。如'abc'、'a123bc'、'abc123'都会转化为0;
2.以数字开头的字符串转换时会进行截取,从第一个字符截取到第一个非数字内容为止。比如'123a'会转换为123,'0123abc'会转换为0123也就是123,'03.8abc'会转换为3.8,其他同理。
根据以上规则,我们可以解释语句3为什么会出现索引丢失的情况了。在查询字段为字段串类型时发生了隐式转换,进而执行全表扫描,导致查询结果存在多条的情况发生。为了避免潜在的问题,我们应该尽量避免在不同数据类型之间进行隐式转换。在设计数据库表时,应该合理选择数据类型,确保相同类型的数据存储在相同类型的列中。如果需要进行数据类型转换,最好明确地使用函数或操作符进行显式转换,以提高查询的可读性和可维护性。
总结起来,MySQL中的varchar和int之间存在隐式转换的机制,可以在一定程度上方便我们进行数据比较和运算。然而,在使用隐式转换时需要注意潜在的问题,并在设计数据库和编写查询时尽量避免过度依赖隐式转换。合理选择数据类型和明确地进行类型转换可以提高查询的效率和可维护性,从而更好地利用MySQL的功能。