一个 MySQL 隐式转换的坑,差点把服务器整崩溃了

news2025/2/21 19:44:07

本来是一个平静而美好的下午,其他部门的同事要一份数据报表临时汇报使用,因为系统目前没有这个维度的功能,所以需要写个SQL马上出一下,一个同事接到这个任务,于是开始在测试环境拼装这条 SQL,刚过了几分钟,同事已经自信的写好了这条SQL,于是拿给DBA,到线上跑一下,用客户端工具导出Excel 就好了,毕竟是临时方案嘛。

就在SQL执行了之后,意外发生了,先是等了一下,发现还没执行成功,猜测可能是数据量大的原因,但是随着时间滴滴答答流逝,逐渐意识到情况不对了,一看监控,CPU已经上去了,但是线上数据量虽然不小,也不至于跑成这样吧,眼看着要跑死了,赶紧把这个事务结束掉了。

什么原因呢?查询的条件和 join 连接的字段基本都有索引,按道理不应该这样啊,于是赶紧把SQL拿下来,也没看出什么问题,于是限制查询条数再跑了一次,很快出结果了,但是结果却大跌眼镜,出来的查询结果并不是预期的。

​经过一番检查之后,最终发现了问题所在,是 join 连接中有一个字段写错了,因为这两个字段有一部分名称是相同的,于是智能的 SQL 客户端给出了提示,顺手就给敲上去了。但是接下来,更让人迷惑了,因为要连接的字段是 int 类型,而写错的这个字段是 varchar 类型,难道不应该报错吗?怎么还能正常执行,并且还有预期外的查询结果?

难道是 MySQL 有 bug 了,必须要研究一下了。

复现当时的情景

假设有两张表,这两张表的结构和数据是下面这样的。

第一张 user表。

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) COLLATE utf8_bin DEFAULT NULL,
  `age` int(3) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

INSERT INTO `user` VALUES (1, '张三', 28, '2022-09-06 07:40:56', '2022-09-06 07:40:59');

​第二张 order表

CREATE TABLE `order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `order_code` varchar(64) COLLATE utf8_bin DEFAULT NULL,
  `money` decimal(20,0) DEFAULT NULL,
  `title` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

INSERT INTO `order` VALUES (1, 2, '1d90530e-6ada-47c1-b2fa-adba4545aabd', 100, 'xxx购买两件商品', '2022-09-06 07:42:25', '2022-09-06 07:42:27');

​目的是查看所有用户的 order 记录,假设数据量比较少,可以直接查,不考虑性能问题。

本来的 SQL 语句应该是这样子的,查询 order表中用户iduser_id在user表的记录。

select o.* from `user` u 
left JOIN `order` o on u.id = o.user_id;

但是呢,因为手抖,将 on 后面的条件写成了 u.id = o.order_code,完全关联错误,这两个字段完全没有联系,而且u.id是 int 类型,o.order_code是varchar类型。

select o.* from `user` u 
left JOIN `order` o on u.id = o.order_code;

这样的话, 当我们执行这条语句的时候,会不会查出数据来呢?

我的第一感觉是,不仅不会查出数据,而且还会报错,因为连接的这两个字段类型都不一样,值更不一样。

结果却被啪啪打脸,不仅没有报错,而且还查出了数据。

可以把这个问题简化一下,简化成下面这条语句,同样也会出现问题。

select * from `order` where order_code = 1;

明明这条记录的 order_code 字段的值是 1d90530e-6ada-47c1-b2fa-adba4545aabd,怎么用 order_code=1的条件就把它给查出来了。

根源所在

相信有的同学已经猜出来了,这里是 MySQL 进行了隐式转换,由于查询条件后面跟的查询值是整型的,所以 MySQL 将 order_code字段进行了字符串到整数类型的转换,而转换后的结果正好是 1。

通过 cast函数转换验证一下结果。

select cast('1d90530e-6ada-47c1-b2fa-adba4545aabd' as unsigned);

再用两条 SQL 看一下字符串到整数类型转换的规则。

select cast('223kkk' as unsigned);
select cast('k223kkk' as unsigned);

​223kkk转换后的结果是 223,而k223kkk转换后的结果是0。总结一下,转换的规则是:

1、从字符串的左侧开始向右转换,遇到非数字就停止;

2、如果第一个就是非数字,最后的结果就是0;

隐式转换的规则

当操作符与不同类型的操作数一起使用的时候,就会发生隐式转换。

例如算数运算符的前后是不同类型时,会将非数字类型转换为数字,比如 '5a'+2,就会将5a转换为数字类型,然后和2相加,最后的结果就是 7 。

再比如 concat函数是连接两个字符串的,当此函数的参数出现非字符串类型时,就会将其转换为字符串,例如concat(88,'就是发'),最后的结果就是 88就是发。

​MySQL 官方文档有以下几条关于隐式转换的规则:

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

也就是两个参数中如果只有一个是NULL,则不管怎么比较结果都是 NULL,而两个 NULL 的值不管是判断大于、小于或等于,其结果都是1。

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

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

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

例如下面这条语句,查询 user 表中name字段是 0x61 的记录,0x是16进制写法,其对应的字符串是英文的 'a',也就是它对应的 ASCII 码。

select * from user where name = 0x61;

所以,上面这条语句其实等同于下面这条

select * from user where name = 'a';

可以用 select 0x61;验证一下。

5、有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 时间戳;

例如下面这两条SQL,都是将条件后面的值转换为时间戳再比较了,只不过

6、有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数(一般默认是 double),则会把 decimal 转换为浮点数进行比较;

在不同的数值类型之间,总是会向精度要求更高的那一个类型转换,但是有一点要注意,在MySQL 中浮点数的精度只有53 bit,超过53bit之后的话,如果后面1位是1就进位,如果是0就直接舍弃。所以超大浮点数在比较的时候其实只是取的近似值。

7、所有其他情况下,两个参数都会被转换为浮点数再进行比较;

如果不符合上面6点规则,则统一转成浮点数再进行运算

避免进行隐式转换

我们在平时的开发过程中,尽量要避免隐式转换,因为一旦发生隐式转换除了会降低性能外, 还有很大可能会出现不期望的结果,就像我最开始遇到的那个问题一样。

之所以性能会降低,还有一个原因就是让本来有的索引失效。

select * from `order` where order_code = 1;

order_code 是 varchar 类型,假设我已经在 order_code 上建立了索引,如果是用“=”做查询条件的话,应该直接命中索引才对,查询速度会很快。但是,当查询条件后面的值类型不是 varchar,而是数值类型的话,MySQL 首先要对 order_code 字段做类型转换,转换为数值类型,这时候,之前建的索引也就不会命中,只能走全表扫描,查询性能指数级下降,搞不好,数据库直接查崩了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/90394.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

职场社交app开发,迎合市场发展需求

互联网时代的到来&#xff0c;让人们在网络上花费的时间越来越多&#xff0c;依赖性也越来越大&#xff0c;我们的生活发生了很大的变化。一个人从出生开始就要面对这个复杂的社会&#xff0c;需要和各种各样的人去交流相处&#xff0c;当我们进入职场之后社交更是变成一种刚需…

【电巢】最新进展 深圳13家国企和民企入局,打造全球电子元器件集散中心

前 言 日前&#xff0c;电子元器件和集成电路国际交易中心创立大会在深圳召开。电巢获悉&#xff0c;该交易中心将于12月底正式揭牌&#xff0c;近期启动试运行&#xff0c;并在试运行期间开启线上交易。 根据此前国家发改委、商务部发布的《关于深圳建设中国特色社会主义先…

[附源码]Python计算机毕业设计高校请假管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

【案例实践】WRF-Python融合技术:WRF 模式前后处理、自动化运行、数据处理、可视化绘图

【查看原文】Python在WRF模型自动化运行及前后处理中实践技术应用 当今从事气象及其周边相关领域的人员&#xff0c;常会涉及气象数值模式及其数据处理&#xff0c;无论是作为业务预报的手段、还是作为科研工具&#xff0c;掌握气象数值模式与高效前后处理语言是一件非常重要的…

我填写“2022年国内软件质量调查问卷”的感想

文章目录感想起因关于软件质量的理解各行各业的质量问题数据质量问题各行各业的质量问题质量问题对我们的影响软件质量软件质量问题的影响有多大软件质量问题软件测试软件测试方法的目的包括&#xff1a;软件的基本测试方法:软件测试的各个阶段测试流程软件测试工程师总结感想起…

免费的进销存系统哪个好一些?

进销存是什么&#xff1f;如何才能选到满意的进销存管理系统&#xff1f; 进销存即购销链条&#xff0c;一般分为“进”“销”“存”“财”四个模块的工作&#xff0c;涉及采购部、物资部、生产部、市场部、销售部、财务部等多部门的协同。通过进销存管理信息化&#xff0c;能…

PDF可以设置哪些编辑限制?

PDF文件可以进行加密&#xff0c;大家都知道&#xff0c;并且加密分为打开密码和编辑限制密码两种。 打开密码&#xff0c;大家都知道是在打开PDF文件的时候需要输入的密码。但是对于编辑限制的认识还不是很清晰。今天和大家一起看一下&#xff0c;PDF编辑限制都可以设置哪些编…

DSP篇--C6701功能调试系列之CAN总线测试

调试的前期准备可以参考前面的博文&#xff1a;DSP篇--C6701功能调试系列之前期准备_nanke_yh的博客-CSDN博客 CAN总线的工作模式有两种&#xff1a;BasicCAN模式和PeliCAN模式。同时&#xff0c;其处理方式又分为&#xff1a;直连和托管两种。 目的&#xff1a;测试CAN总线的…

SQL语句练习04

目录 一、见表并插入数据 二、查询语句的练习 一、见表并插入数据 一、建立如下故事表(命名格式“姓名拼音_三位学号 _story”&#xff0c;如LBJ_023_story&#xff09;&#xff0c;并插入数据 、create table LYL_116_story(sid varchar(7),sname varchar(12), snation var…

华为云-PaaS云服务

文章目录1、什么是PaaS2、云服务三剑客2.1、 IaaS2.2、 PaaS2.3、 SaaS2.4、三剑客分布2.5 摩天大楼之下的三剑客3、华为PasS平台3.1、功能支持4、总结1、什么是PaaS Platform-as-a-Service&#xff08;平台即服务&#xff09;&#xff0c;它作为云服务之一&#xff0c;平台也…

发布变更又快又稳?腾讯运维工程师经验首发

导读| 如何让功能缺陷修复快速上线&#xff1f;版本发出问题时怎样快速回退&#xff1f;效率提升后质量掉队&#xff1f;为解决这些常让运维工程师头疼的事情&#xff0c;本栏目特邀腾讯知名运维工程师袁旭东&#xff0c;讲述对象存储COS的发布演进过程&#xff0c;为各位开发者…

welecome

欢迎使用Markdown编辑器你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持&#xff0c;除…

循环神经网络(MLP——>RNN)

n元语法模型&#xff0c; 其中单词xt在时间步t的条件概率仅取决于前面n−1个单词。 对于时间步t−(n−1)之前的单词&#xff0c; 如果我们想将其可能产生的影响合并到xt上&#xff0c; 需要增加n&#xff0c;然而模型参数的数量也会随之呈指数增长&#xff0c; 因为词表V需要存…

git将其他分支的某个提交合到当前分支

目录1. 命令2. 操作说明1. 命令 同步一个提交的命令&#xff1a;git cherry-pick -x 提交id 同步多个提交的命令&#xff1a;git cherry-pick -x 提交id1 提交id2 提交id3 ... -x 可加可不加&#xff0c;一般建议加&#xff0c;因为我们这次的提交会产生新在 commit ID&#…

【自定义maven骨架】IDEA如何自定义一个的maven骨架,解决maven骨架添加之后不显示的问题

目录 一、自定义maven骨架 1.1、创建maven工程 1.2、引入archetype骨架插件 1.3、执行创建骨架命令 1.4、执行install命令 1.5、执行crawl命令 1.6、添加自定义骨架 1.7、解决maven骨架不生效问题 这篇文章&#xff0c;主要介绍一下如何使用IDEA自定义一个maven骨架项目…

位图(bitset)的使用【STL】

文章目录1. 介绍1.1 背景1.2 概念1.3 应用2. 位图的使用2.1 原型2.2 构造位图2.3 常用接口2.4 示例2.4 常用运算符2.4.1 >>和<<2.4.2 赋值运算符、关系运算符、复合赋值运算符、单目运算符2.4.3 位运算符2.4.4 [ ]运算符1. 介绍 1.1 背景 一道面试题&#xff1a;…

读懂ShuffleNet V2

ShuffleNetV2介绍 https://arxiv.org/abs/1807.11164 深度卷积神经网络的架构创新显著的提升了在ImageNet数据集上的分类准确率&#xff0c;如VGG、GoogleNet、ResNet、DenseNet、ResNeXt、SE-Net以及自动网络架构搜索获得的方案。然而除了准确率&#xff0c;计算复杂度是另一…

实操分享:台式数字万用表测电压,手动测试VS万用表软件NS-Multimeter

台式数字万用表可以测量电流、电压、电阻、温度等多种参数&#xff0c;是电子工程师必备的仪器之一。本篇文章纳米软件Namisof小编将为大家分享&#xff1a;使用台式数字万用表手动测电压和万用表软件测电压的方法。本次将用DMM6500台式数字万用表为大家进行演示说明。 一、DMM…

大学生图书馆网页设计模板代码 DIV布局书店网页作业成品 学校书籍网页制作模板 学生简单书籍阅读网站设计成品

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

ESP-C3入门1. VSCode+IDF 开发环境

ESP-C3入门1. VSCodeIDF 开发环境一、芯片说明1. 主要参数2. 内部结构图ESP32-C3-MINI-1内部架构图&#xff1a;ESP32-C3-MINI-1U内部架构图&#xff1a;3. 引脚4. 引脚描述5. strapping管脚6. 系统复位二、idf-vscode开发环境搭建1. 安装vscode2. idf配置&#xff08;1&#x…