导读

作者徐晨亮,

MySQL DBA,知数堂学员。热衷于数据库优化,自动化运维及数据库周边工具开发,对

MySQL源码有一定的兴趣

本文建议横屏观看,效果更佳

一、问题描述

[email protected] 22:12: [xucl]> show create table t1\G

*************************** 1. row ***************************

Table: t1

Create Table: CREATE TABLE `t1` (

`id` varchar(255) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

[email protected] 22:19: [xucl]> select * from t1;

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

| id |

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

| 204027026112927605 |

| 204027026112927603 |

| 2040270261129276 |

| 2040270261129275 |

| 100 |

| 101 |

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

6 rows in set (0.00 sec)

奇怪的现象:

[email protected] 22:19: [xucl]> select * from t1 where id=204027026112927603;

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

| id |

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

| 204027026112927605 |

| 204027026112927603 |

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

2 rows in set (0.00 sec)

什么鬼,明明查的是204027026112927603,为什么204027026112927605也出来了

二、源码解释

堆栈调用关系如下所示:

其中JOIN::exec()是执行的入口,Arg_comparator::compare_real()是进行等值判断的函数,其定义如下

int Arg_comparator::compare_real()

{

/*

Fix yet another manifestation of Bug#2338. 'Volatile' will instruct

gcc to flush double values out of 80-bit Intel FPU registers before

performing the comparison.

*/

volatile double val1, val2;

val1= (*a)->val_real();

if (!(*a)->null_value)

{

val2= (*b)->val_real();

if (!(*b)->null_value)

{

if (set_null)

owner->null_value= 0;

if (val1 < val2) return -1;

if (val1 == val2) return 0;

return 1;

}

}

if (set_null)

owner->null_value= 1;

return -1;

}

比较步骤如下图所示,逐行读取t1表的id列放入val1,而常量204027026112927603存在于cache中,类型为double类型(2.0402702611292762E+17),所以到这里传值给val2后val2=2.0402702611292762E+17。

当扫描到第一行时,204027026112927605转成doule的值为2.0402702611292762e17,等式成立,判定为符合条件的行,继续往下扫描,同理204027026112927603也同样符合

如何检测string类型的数字转成doule类型是否溢出呢?这里经过测试,当数字超过16位以后,转成double类型就已经不准确了,例如20402702611292711会表示成20402702611292712(如图中val1)

MySQL string转成double的定义函数如下:

{

char buf[DTOA_BUFF_SIZE];

double res;

DBUG_ASSERT(end != NULL && ((str != NULL && *end != NULL) ||

(str == NULL && *end == NULL)) &&

error != NULL);

res= my_strtod_int(str, end, error, buf, sizeof(buf));

return (*error == 0) ? res : (res < 0 ? -DBL_MAX : DBL_MAX);

}

真正转换函数my_strtod_int位置在dtoa.c(太复杂了,简单贴个注释吧)

/*

strtod for IEEE--arithmetic machines.

This strtod returns a nearest machine number to the input decimal

string (or sets errno to EOVERFLOW). Ties are broken by the IEEE round-even

rule.

Inspired loosely by William D. Clinger's paper "How to Read Floating

Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].

Modifications:

1. We only require IEEE (not IEEE double-extended).

2. We get by with floating-point arithmetic in a case that

Clinger missed -- when we're computing d * 10^n

for a small integer d and the integer n is not too

much larger than 22 (the maximum integer k for which

we can represent 10^k exactly), we may be able to

compute (d*10^k) * 10^(e-k) with just one roundoff.

3. Rather than a bit-at-a-time adjustment of the binary

result in the hard case, we use floating-point

arithmetic to determine the adjustment to within

one bit; only in really hard cases do we need to

compute a second residual.

4. Because of 3., we don't need a large table of powers of 10

for ten-to-e (just some small tables, e.g. of 10^k

for 0 <= k <= 22).

*/

既然是这样,我们测试下没有溢出的案例

[email protected] 23:30: [xucl]> select * from t1 where id=2040270261129276;

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

| id |

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

| 2040270261129276 |

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

1 row in set (0.00 sec)

[email protected] 23:30: [xucl]> select * from t1 where id=101;

+------+

| id |

+------+

| 101 |

+------+

1 row in set (0.00 sec)

结果符合预期,而在本例中,正确的写法应当是

[email protected] 22:19: [xucl]> select * from t1 where id='204027026112927603';

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

| id |

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

| 204027026112927603 |

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

1 row in set (0.01 sec)

三、结论

避免发生隐式类型转换,隐式转换的类型主要有字段类型不一致、in参数包含多个类型、字符集类型或校对规则不一致等

隐式类型转换可能导致无法使用索引、查询结果不准确等,因此在使用时必须仔细甄别

数字类型的建议在字段定义时就定义为int或者bigint,表关联时关联字段必须保持类型、字符集、校对规则都一致

最后贴一下官网对于隐式类型转换的说明吧

1、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.

2、If both arguments in a comparison operation are strings, they are compared as strings.

3、If both arguments are integers, they are compared as integers.

4、Hexadecimal values are treated as binary strings if not compared to a number.

5、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.

6、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.

7、In all other cases, the arguments are compared as floating-point (real) numbers.

参考文章

2、Type Conversion in Expression Evaluation:https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html

感谢八怪的友情指导,想学习更多源码内容,强烈推荐一下八怪的专栏《深入理解MySQL主从原理》

END

点击下图小程序订阅

《深入理解MySQL主从原理32讲》专栏

可了解更多八怪技术文章

扫码加入MySQL技术Q群

(群号:793818397)

mysql to double_MySQL令人咋舌的隐式转换 - 惊觉...相关推荐

  1. mysql with ties_MySQL令人咋舌的隐式转换

    二.源码解释 堆栈调用关系如下所示: 其中JOIN::exec()是执行的入口,Arg_comparator::compare_real()是进行等值判断的函数,其定义如下 int Arg_compa ...

  2. MySQL令人咋舌的隐式转换

    一.问题描述 root@mysqldb 22:12: [xucl]> show create table t1\G *************************** 1. row **** ...

  3. MySQL和Oracle中的隐式转换

    今天在处理一个问题的时候,需要根据其他部门提供的sql语句对一个表中的数据进行了筛查. 语句类似下面的形式 > SELECT MAX_LEVEL,LOGOUT_TIME,CURRENT_DATE ...

  4. 在将varchar值id转换为int时失败_MySQL令人咋舌的隐式转换

    导读 作者徐晨亮,MySQL DBA,知数堂学员.热衷于数据库优化,自动化运维及数据库周边工具开发,对MySQL源码有一定的兴趣 本文建议横屏观看,效果更佳 一.问题描述 root@mysqldb 2 ...

  5. MySQL中varchar类型字段隐式转换造成多删除数据

    例如一个表中字段是varchar类型: desc test; +-------+-------------+------+-----+---------+----------------+ | Fie ...

  6. mysql 隐式失误_评“MySQL 隐式转换引起的执行结果错误”

    今天看到一篇关于MySQL隐式转换引发执行结果错误的文章: ====================================================================== ...

  7. mysql数据库隐式表_详解MySQL数据库常见的索引问题:无索引,隐式转换,附实例说明...

    概述 在这些年的工作之中,由于SQL问题导致的数据库故障层出不穷,而索引问题是SQL问题中出现频率最高的,常见的索引问题包括:无索引,隐式转换. 索引问题 1.无索引 当数据库中出现访问表的SQL无索 ...

  8. mysql隐式转换造成索引失效的事故总结

    隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误. 由于表的字段tu_mdn定义为varchar2(20),但在查询时把该字段作为number类型以where条件传给mysql,这样 ...

  9. MySQL 隐式转换 字符串和整型说明

    MySQL 隐式转换 字段类型定义 CREATE TABLE `user` (`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '编号',/* ...... * ...

最新文章

  1. AB1601蓝牙初始化注意事项
  2. Python模拟浏览器实现网页访问
  3. 在Android中使用Protocol Buffers
  4. mysql错误码 1045_MySql 错误代码 1045
  5. hp chromebook11 linux,又有18款Chromebook即将支持Linux应用
  6. Kubernetes 学习总结(20)—— Kubernetes 与微服务和容器之间是什么关系?
  7. GIS制图课程目录(持续整理)
  8. vue引入图片url变量
  9. 利用qiskit实现量子门以及态的初始化
  10. UVALive 6437 Power Plant 【最小生成树 + 思维】
  11. ros之tf简介[tf-Package Summary]
  12. 作业三:代码规范、代码复审、PSP
  13. linux驱动篇-touchscreen-精简版
  14. 天翼云linux版本,天翼云Linux主机操作
  15. u8系统服务器已停止怎么办,紧急通知!用友U8报错:enterpriseportal 已停止工作的解决办法!...
  16. 数据库--MYSQL高级(多表),数据库的完整性,约束,数据类型,多表实现 CRUD 操作
  17. fiddler移动端抓包wifi不可上网(配置都正确,防火墙已关闭)
  18. PHP中获取当前页面的URL信息
  19. 磁盘划分空间提示磁盘上没有足够的空间完成此操作,使用傲梅分区助手解决
  20. linux windows 垃圾清理,windows垃圾清理

热门文章

  1. [Vuex系列] - Vuex中的getter的用法
  2. 课堂练习---统计空格流程图、Jackson图
  3. Android—将Bitmap图片保存到SD卡目录下或者指定目录
  4. 九种浏览器端缓存机制知多少(转)
  5. c语言中if—else的配对问题
  6. Mac上因磁盘格式导致gulp无限刷新问题
  7. 0112作业 字符串练习
  8. C#中使用代码动态改变配置文件信息
  9. (转)Engineering Productivity
  10. mysql 简单优化规则