为了复现生产环境产生的Bug,造了几条数据记录:
CREATE TABLE `test` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=190 DEFAULT CHARSET=utf8mb4;
INSERT INTO test(id,user_id)
VALUES
(1,'123abc'),(2,'456def'),(3,'789igk'),(4,'0');
select * from test;
select * from test where user_id = 0;
这里建表时‘user_id’使用的时varchar类型,但在where条件头使用数字进行匹配,当没有满足Mysql隐式转换时,改语句不会出现Bug,但依然会存在性能问题,当user_id存在索引时,此语句将不会走索引而进行全表扫描。
先插入几条记录INSERT INTO test(id,user_id) VALUES (5,'012abc'),(6,'0abdef'),(7,'asdigk');
在执行一次全表查询语句
此时表中存在7条记录了,在次执行问题SQL
SELECT * from test WHERE user_id = 0;
经过反复验证与资料查询,在官网有这么一段话
The following rules describe how conversion occurs for comparison operations:
If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.
If both arguments in a comparison operation are strings, they are compared as strings.
If both arguments are integers, they are compared as integers.
Hexadecimal values are treated as binary strings if not compared to a number.
If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. This is not done for the arguments to IN(). To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.
A single-row subquery from a table or tables is not considered a constant. For example, if a subquery returns an integer to be compared to a DATETIME value, the comparison is done as two integers. The integer is not converted to a temporal value. To compare the operands as DATETIME values, use CAST() to explicitly convert the subquery value to DATETIME.
If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.
In all other cases, the arguments are compared as floating-point (real) numbers.
顺便粘贴下官网地址,有兴趣自己继续研究
https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html
顺便加下自己反复验证的结果,当where后查询条件所得值与建表时使用不同类型时,会产生Mysql的隐式类型转换;本人只验证了当表中为varchar类型,传入字段为int类型时的情况;验证结果与猜测(如有错误,请大拿指出):
1、当字符串以数字开头时的情况:
'000aaa': user_id = 0 查询结果为真
'001aaa': user_id = 0 查询结果为假
'100aaa': user_id = 0 查询结果为假
结论:当以数字开头时,Mysql会自动截取至第一位非数字字符,然后更后面的值进行比较
2、当字符串以非数字开头时:
'abc100': user_id = 0 查询结果为真
'`0abcd': user_id = 0 查询结果为真
'$0abc0': user_id = 0 查询结果为真
结论:当以非数字开头时,查询结果都为真