MySQL知识点之隐式类型转换

平时工作中研发对数据列的类型定义和使用过程中没有足够重视,轻则导致性能不好,重则导致数据不准影响业务,下面列举一些MySQL隐式类型转换所导致的问题,希望能对各位有所帮助。

假设查询表tb001所有数据得到结果如下:



那么执行SELECT * FROM TB001 WHERE C1=1.1111会是啥结构呢?
选项1:返回所有记录
选项2:返回id为1的记录
选项3:不返回任何记录
选项4:以上都不对

这似乎是个送分题,必须选2,真的如此么?
当表结构定位为:
CREATE TABLE tb001 (
id int(11) NOT NULL AUTO_INCREMENT,
c1 float DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
那么执行结果为:


当表结构定位为:
CREATE TABLE tb001 (
id int(11) NOT NULL AUTO_INCREMENT,
c1 DECIMAL(18,4) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8
那么执行结果为:
image.png

通过上面的测试,发现列的类型不同,会影响到执行结果,那么假设表定于为:
CREATE TABLE tb001 (
id int(11) NOT NULL AUTO_INCREMENT,
c1 int(11) DEFAULT NULL,
PRIMARY KEY (id),
KEY IDX_C1 (c1)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
查询表tb001所有数据得到结果如下:
image.png

那么执行SELECT * FROM TB001 WHERE C1=1.5;会得到什么结果呢?
image.png

上面的执行结果虽然有点出乎意料,但是也可以解释,把1.5四舍五入得到2,然后按照C1=2条件得到ID=2的记录。
那么将等于改为小于等于呢?即SELECT * FROM TB001 WHERE C1<=1.5;会得到什么结果呢?
image.png

可以将上面的查询解释为把1.5强行转换为1,按照C1=1条件得到ID=1的记录。
但上面两种解释有点冲突,如何选取呢?难道小于等于不包含等于么?

因为测试表数据仅有3行,全表扫描比索引查找更有效,于是删除C1上索引继续查询。


aa.png

可以惊奇地发现,查询结果又变啦,对于等于查询,到底把1.5转换成什么数据呢?
再看另外例子,假设测试数据脚本为:
CREATE TABLE t_user(
user_id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(20),
last_name VARCHAR(20),
full_name VARCHAR(50)
);
INSERT INTO t_user(first_name,last_name,full_name)VALUES('xiao','ming','xiao ming');
INSERT INTO t_user(first_name,last_name,full_name)VALUES('zhang','san','zhang san');
INSERT INTO t_user(first_name,last_name,full_name)VALUES('li','si','li si');
全表数据为:


image.png

然后想按照full_name进行查询:
image.png

明明只是想查下全名叫"xiao ming"的用户,为啥全部返回呢?

一定是打开方式不对,那么为啥不对呢?

在所有关系型数据库中,每个列都有类型定义,通过CAST或CONVERT函数将数据从一种类型转换为另外一种类型,如SELECT CAST('2018-01-01' AS DATETIME),被称为“显式类型转换“。与”显示类型转换“相对的是”隐式类型转换“,在按照时间字段进行查询时,我们通常会直接使用时间字符串作为参数,数据库会自动地将该字符串转换为时间类型,然后再与时间字段进行比较,这便是”隐式类型转换“。

当在MySQL中对两种不同类型数据进行比较或计算时,就会导致类型转换,其中一部分类型转换属于正常操作,另外一部分就属于“异常操作”,如查询条件为WHERE full_name='xiao'+' '+'ming'时,首先在MySQL中字符串不能使用+来连接,只有数值类型才能使用+来进行计算,因此MySQL会尝试把'xiao'/' '/'ming'三个字符串转换为数字,转化失败就当做0来处理,因此'xiao'+' '+'ming'的结果为0,然后再把full_name列和0做比较,由于full_name时VARCHAR,而0为数值,因此将full_name列转换为float,再次转换失败当做0来处理,因为0=0,所以所有记录都满足。

PS1:当发生隐式类型转换且导致数据发生变化丢失时,查询不会抛出异常,仅会产生警告。如果上面的SELECT操作被修改为DELETE操作,则会导致全表数据被删除,忽略这些警告会导致故障被延期发现,从而引发更严重的问题。
MySQL发生隐式转换的规则如下:

  1. 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换

  2. 两个参数都是字符串,会按照字符串来比较,不做类型转换

  3. 两个参数都是整数,按照整数来比较,不做类型转换

  4. 十六进制的值和非数字做比较时,会被当做二进制串

  5. 有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp

  6. 有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较

  7. 所有其他情况下,两个参数都会被转换为浮点数再进行比较
    在上面的测试中,很多查询实际上是因为发生了隐式类型转换,数据被转换为浮点数进行比较,而浮点数最大的问题就是无法精确表示数据,也就无法“正确”地比较两个“浮点数”是否相同。如果表中数值列上有索引,那么即使需要对该列进行类型转换,也能使用索引来“优化查询”,上面示例中会将INT列隐式转换为“float”来进行比较,INT列是否存在索引会导致查询扫描全表数据或部分数据,最终导致查询差异。
    当数据列定于为FLOAT且不指定不指定Float的长度和小数位数时,由于Float是浮点数,在MySQL中存储的是近似值,因此无法使用精确查找进行匹配,所以上面示例中WHERE C1=1.1111的执行返回数据为空,查询显示警告信息Empty set.

解决办法:

  1. 将Float数据类型转换为Double或Decimal数据类型,Decimal数据类型会保留准确精确度数据,而使用Double时不存在该问题。

  2. 为Float指定长度和小数位数

  3. 使用FORMAT函数进行转换,如WHERE FORMAT(C1,3)=FORMAT(123.456,3)

  4. 使用Like进行匹配,如WHERE C1 LIKE 123.456

参考链接:

https://dev.mysql.com/doc/refman/8.0/en/problems-with-float.html

https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html

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

推荐阅读更多精彩内容