这次被坑惨了,MySQL的隐式转换导致了一个线上BUG

这次被坑惨了,MySQL的隐式转换导致了一个线上BUG

Java架构师技术栈分享 2020-07-23 13:39:54

某一天,开发问我,为什么针对一个查询会有两条记录,且其中一条记录并不符合条件select * from tablea where xxno = 170325171202362928;xxno为 170325171202362928 和 170325171202362930的都出现在结果中。

一个等值查询为什么会有另外一个不同值的记录查询出来呢?

我们一起来看看究竟!

分析

我们查看该表结构,发现xxno 为varchar 类型,但是等号右边是一个数值类型,这种情况下MySQL会如何进行处理呢?官方文档如下:https://dev.mysql.com/doc/refman/5.6/en/type-conversion.html

The following rules describe how conversion occurs for comparison operations: .... 省略一万字 .... In all other cases, the arguments are compared as floating-point (real) numbers.

也就是说,他会将等于号的两边转换成浮点数来做比较。

Comparisons that use floating-point numbers (or values that are converted to floating-point numbers) are approximate because such numbers are inexact. This might lead to results that appear inconsistent:

如果比较使用了浮点型,那么比较会是近似的,将导致结果看起来不一致,也就是可能导致查询结果错误。

我们测试下刚刚生产的例子:

mysql > select '170325171202362928' = 170325171202362930;

+-------------------------------------------+

| '170325171202362928' = 170325171202362930 |

+-------------------------------------------+

|                                         1 |

+-------------------------------------------+

1 row in set (0.00 sec)```

可以发现,字符串的'170325171202362928' 和 数值的170325171202362930比较竟然是相等的。我们再看下字符串'170325171202362928' 和字符串'170325171202362930' 转化为浮点型的结果

mysql  > select'170325171202362928'+0.0;+--------------------------+|'170325171202362928'+0.0|+--------------------------+|1.7032517120236294e17|+--------------------------+1row inset(0.00sec)mysql > select '170325171202362930'+0.0;+--------------------------+|'170325171202362930'+0.0|+--------------------------+|1.7032517120236294e17|+--------------------------+1row inset(0.00sec)

我们发现,将两个不同的字符串转化为浮点数后,结果是一样的,

所以只要是转化为浮点数之后的值是相等的,那么,经过隐式转化后的比较也会相等,我们继续进行测试其他转化为浮点型相等的字符串的结果

mysql > select'170325171202362931'+0.0;+--------------------------+|'170325171202362931'+0.0|+--------------------------+|1.7032517120236294e17|+--------------------------+1row inset(0.00sec)mysql > select '170325171202362941'+0.0;+--------------------------+|'170325171202362941'+0.0|+--------------------------+|1.7032517120236294e17|+--------------------------+1row inset(0.00sec)

字符串'170325171202362931'和'170325171202362941'转化为浮点型结果一样,我们看下他们和数值的比较结果

mysql > select'170325171202362931'=170325171202362930;+-------------------------------------------+|'170325171202362931'=170325171202362930|+-------------------------------------------+|1|+-------------------------------------------+1row inset(0.00sec)mysql > select '170325171202362941'=170325171202362930;+-------------------------------------------+|'170325171202362941'=170325171202362930|+-------------------------------------------+|1|+-------------------------------------------+1row inset(0.00sec)

结果也是符合预期的。

因此,当MySQL遇到字段类型不匹配的时候,会进行各种隐式转化,一定要小心,有可能导致精度丢失。

For comparisons of a string column with a number, MySQL cannot use an index on the column to look up the value quickly. If str_col is an indexed string column, the index cannot be used when performing the lookup in the following statement:

如果字段是字符型,且上面有索引的话,如果查询条件是用数值来过滤的,那么该SQL将无法利用字段上的索引

SELECT * FROM tbl_name WHERE str_col=1;

The reason for this is that there are many different strings that may convert to the value 1, such as '1', ' 1', or '1a'.

我们进行测试

mysql > create table tbl_name(id int ,str_col varchar(10),c3 varchar(5),primary key(id),key idx_str(str_col));

Query OK, 0 rows affected (0.02 sec)

mysql  > insert into tbl_name(id,str_col) values(1,'a'),(2,'b');

Query OK, 2 rows affected (0.01 sec)

Records: 2  Duplicates: 0  Warnings: 0

mysql  > insert into tbl_name(id,str_col) values(3,'3c'),(4,'4d');

Query OK, 2 rows affected (0.00 sec)

Records: 2  Duplicates: 0  Warnings: 0

mysql  > desc select * from tbl_name where str_col='a';

+----+-------------+----------+------+---------------+---------+---------+-------+------+--------------------------+

| id | select_type | table    | type | possible_keys | key     | key_len | ref   | rows | Extra                    |

+----+-------------+----------+------+---------------+---------+---------+-------+------+--------------------------+

|  1 | SIMPLE      | tbl_name | ref  | idx_str       | idx_str | 13      | const |    1 | Using where; Using index |

+----+-------------+----------+------+---------------+---------+---------+-------+------+--------------------------+

mysql  > desc select * from tbl_name where str_col=3;

+----+-------------+----------+------+---------------+------+---------+------+------+-------------+

| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |

+----+-------------+----------+------+---------------+------+---------+------+------+-------------+

|  1 | SIMPLE      | tbl_name | ALL  | idx_str       | NULL | NULL    | NULL |    4 | Using where |

+----+-------------+----------+------+---------------+------+---------+------+------+-------------+

1 row in set (0.00 sec)

mysql [localhost] {msandbox} (test) > select * from tbl_name where str_col=3;

+----+---------+------+

| id | str_col | c1   |

+----+---------+------+

|  3 | 3c      | NULL |

+----+---------+------+

1 row in set, 2 warnings (0.00 sec)

同时我们可以看到,我们用数值型的3和str_col进行比较的时候,他无法利用索引,同时取出来的值也是错误的,

mysql  > show warnings;

+---------+------+----------------------------------------+

| Level   | Code | Message                                |

+---------+------+----------------------------------------+

| Warning | 1292 | Truncated incorrect DOUBLE value: '3c' |

| Warning | 1292 | Truncated incorrect DOUBLE value: '4d' |

+---------+------+----------------------------------------+

MySQL针对3c 和 4d这两个值进行了转化,变成了3和4

小结

在数据库中进行查询的时候,不管是Oracle还是MySQL,一定要注意字段类型,杜绝隐式转化,不仅会导致查询缓慢,还会导致结果错误。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342