这几天线上出现一个诡异的更新问题,看下面这个测试案例,主看看表结构及更新结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
mysql> select * from t1; +------+----------------------+ | id | c | +------+----------------------+ | 1 | 20190309000002958094 | | 2 | 20190309000002958095 | | 3 | 20190309000002958096 | +------+----------------------+ 3 rows in set (0.00 sec) mysql> show create table t1\G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) DEFAULT NULL, `c` varchar(100) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.01 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update t1 set id = 10 where c = 20190309000002958094; Query OK, 3 rows affected (0.00 sec) Rows matched: 3 Changed: 3 Warnings: 0 mysql> select * from t1; +------+----------------------+ | id | c | +------+----------------------+ | 10 | 20190309000002958094 | | 10 | 20190309000002958095 | | 10 | 20190309000002958096 | +------+----------------------+ 3 rows in set (0.00 sec) |
我明明想更新一行,但更新了三行。也就是说 c = 20190309000002958094 匹配上了 20190309000002958095 和 20190309000002958096 的值。
其主要原因还是更新条件由于忘记指定为字符串了,在 MySQL 内部产生了隐式转换。所以可以看出隐式转换不光会造成无法使用索引,还会导致数据更新出现非理想情况(推荐使用 SOAR 工具对你的 SQL 进行检测,评测结果会告诉你产生了隐式转换)。
跟一个朋友 @韩川川 讨论验证之后,感觉应该是把 string 转换为了 double 类型(网上也有很多此类错误 Truncated incorrect DOUBLE value),double 是双精度非准确浮点类型,当超过一定长度后会进行四舍五入。验证方式如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
mysql> create table t2(id double); Query OK, 0 rows affected (0.03 sec) mysql> insert into t2 select 20190309000002958094; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> insert into t2 select 20190309000002958095; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> insert into t2 select 20190409000002958095; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> select * from t2; +-----------------------+ | id | +-----------------------+ | 2.0190309000002957e19 | | 2.0190309000002957e19 | | 2.0190409000002957e19 | +-----------------------+ 3 rows in set (0.00 sec) |
从结果来看,插入的值虽然不同,但存入 double 后都变成一样的了,也就是间接说明了前面为什么会把所有相关值都匹配上。我们直接对比数值和字符串看看结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> select 20190309000002958095 = '20190309000002958095'; +-----------------------------------------------+ | 20190309000002958095 = '20190309000002958095' | +-----------------------------------------------+ | 1 | +-----------------------------------------------+ 1 row in set (0.00 sec) mysql> select 20190309000002958095 = '20190309000002958096'; +-----------------------------------------------+ | 20190309000002958095 = '20190309000002958096' | +-----------------------------------------------+ | 1 | +-----------------------------------------------+ 1 row in set (0.00 sec) |
值虽然不同,但由于一个是数值,一个是字符串,MySQL 比较之后认为是相等的。
以上推测可能不正确的,也没有再深入的能力了。如果有更详细和正确的答案,欢迎指教。
<参考>
https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html