看看 MySQL令人咋舌的隐式转换

mysql教程栏目介绍相关的隐式转换

看看 MySQL令人咋舌的隐式转换

更多相关免费学习推荐:mysql教程(视频)

一、问题描述

root@mysqldb 22:12:  [xucl]> show create table t1G*************************** 1. row ***************************       Table: t1Create Table: CREATE TABLE `t1` (  `id` varchar(255) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)root@mysqldb 22:19:  [xucl]> select * from t1;+--------------------+| id                 |+--------------------+| 204027026112927605 || 204027026112927603 || 2040270261129276   || 2040270261129275   || 100                || 101                |+--------------------+6 rows in set (0.00 sec)

登录后复制

奇怪的现象:

root@mysqldb 22:19:  [xucl]> select * from t1 where id=204027026112927603;+--------------------+| id                 |+--------------------+| 204027026112927605 || 204027026112927603 |+--------------------+2 rows in set (0.00 sec)

登录后复制

640?wx_fmt=jpeg

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

二、源码解释

堆栈调用关系如下所示:

640?wx_fmt=jpeg

其中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 null_value= 1;  return -1;}

登录后复制

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

640?wx_fmt=jpeg

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

640?wx_fmt=jpeg

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

640?wx_fmt=jpeg

640?wx_fmt=jpeg

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 

真正转换函数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 

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

root@mysqldb 23:30:  [xucl]> select * from t1 where id=2040270261129276;+------------------+| id               |+------------------+| 2040270261129276 |+------------------+1 row in set (0.00 sec)root@mysqldb 23:30:  [xucl]> select * from t1 where id=101;+------+| id   |+------+| 101  |+------+1 row in set (0.00 sec)

登录后复制

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

root@mysqldb 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 aconstant, the constant is converted to a timestamp before the comparison is performed. This isdone to be more ODBC-friendly. This is not done for the arguments to IN(). To be safe, alwaysuse complete datetime, date, or time strings when doing comparisons. For example, to achieve bestresults when using BETWEEN with date or time values, use CAST() to explicitly convert the values tothe desired data type.A single-row subquery from a table or tables is not considered a constant. For example, if a subqueryreturns 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. Thearguments are compared as decimal values if the other argument is a decimal or integer value, or asfloating-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.

登录后复制

以上就是看看 MySQL令人咋舌的隐式转换的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/1736675.html

(0)
上一篇 2025年2月19日 21:10:56
下一篇 2025年2月19日 21:11:12

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • php如何原生查询mysql

    php原生查询mysql的操作是:1、创建一个php示例文件;2、连接数据库;3、检查链接是否成功;4、执行查询语句;5、处理查询结果;6、关闭数据库连接即可。 本教程操作系统:Windows10系统、php8.1.3版本、Dell G3电…

    2025年2月23日
    100
  • 怎么修改php和mysql端口

    通过修改PHP和MySQL的配置文件修改php和mysql端口。详细介绍:1、修改PHP端口,打开PHP配置文件php.ini,在php.ini文件中查找“; Listen for TCP/IP connections on port; h…

    2025年2月23日
    100
  • mysql在php显示乱码怎么解决

    mysql在php显示乱码可以通过设置数据库字符集、设置PHP页面字符集、设置MySQL数据库和表的字符集以及进行编码转换等方法来解决。详细介绍:1、设置数据库字符集,通过设置字符集,可以确保从数据库中获取的数据以正确的编码显示在网页上;2…

    2025年2月23日
    100
  • php操作mysql数据库的扩展有哪些

    PHP 提供多种 MySQL 扩展:MySQLi:推荐用于 PHP 5.3 及以上版本,提供面向对象接口和高性能。PDO:可访问多种数据库的通用抽象层, 提供一致的接口。MySQLdb:已弃用,已被 MySQLi 取代。选择扩展时,应考虑 …

    2025年2月23日
    100
  • 使用PHP开发web应用程序有哪些优势

    PHP在Web应用程序开发中的优势:1. 开源免费;2. 易于学习;3. 丰富的库和框架;4. 跨平台兼容性;5. 与数据库无缝集成;6. 高效性能;7. 庞大的社区支持;8. 广泛的行业应用。 PHP开发Web应用程序的优势 PHP作为一…

    2025年2月23日
    100
  • php制作地图数据库应该储存哪些属性

    PHP 地图数据库属性包括:几何属性:表示要素形状(geom)。非几何属性(元数据):唯一标识符(id)、名称(name)、描述(description)、类型(type)、其他属性(properties)、标签(tags)、可见性(vis…

    2025年2月23日
    100
  • 在使用php时需要用到哪些软件

    成功使用 PHP 所需软件:文本编辑器或集成开发环境(IDE)Web 服务器PHP 解释器数据库管理系统(DBMS) 使用 PHP 所需软件 PHP (超文本预处理语言) 是一种广泛用于 Web 开发的脚本语言。为了成功使用 PHP,您需要…

    2025年2月23日
    100
  • php程序员需要掌握的技能有哪些

    PHP 程序员必备技能:1. 核心 PHP 语言基础,包括语法、数据类型、控制流和函数;2. 面向对象编程;3. Web 开发:HTML、CSS、JavaScript、HTTP 协议、服务器端编程;4. 数据库:SQL 查询语言、DBMS、…

    2025年2月23日
    100
  • PHP高级工程师具备哪些技能

    PHP 高级工程师必备技能包括:精通 PHP 编程语言,包括其核心概念和语法。掌握流行的 PHP 框架和库。熟练数据库管理,包括关系型和非关系型数据库。擅长性能优化和云计算。具备沟通、团队合作、解决问题和批判性思维等软技能。持续关注职业发展…

    2025年2月23日
    100
  • php如何配置mysql

    为 PHP 配置 MySQL 需要按以下步骤操作:安装 MySQL 扩展。编辑 php.ini 配置文件,启用扩展。创建包含 MySQL 连接信息的凭证文件。在 PHP 脚本中使用 mysqli_connect() 函数连接到 MySQL。…

    2025年2月23日
    100

发表回复

登录后才能评论