这次被坑惨了,MySQL的隐式转换导致了一个线上BUG
某一天,开发问我,为什么针对一个查询会有两条记录,且其中一条记录并不符合条件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 |
+--------------------------+
1 row in set (0.00 sec)
mysql > select '170325171202362930'+0.0;
+--------------------------+
| '170325171202362930'+0.0 |
+--------------------------+
| 1.7032517120236294e17 |
+--------------------------+
1 row in set (0.00 sec)
我们发现,将两个不同的字符串转化为浮点数后,结果是一样的,
所以只要是转化为浮点数之后的值是相等的,那么,经过隐式转化后的比较也会相等,我们继续进行测试其他转化为浮点型相等的字符串的结果
mysql > select '170325171202362931'+0.0;
+--------------------------+
| '170325171202362931'+0.0 |
+--------------------------+
| 1.7032517120236294e17 |
+--------------------------+
1 row in set (0.00 sec)
mysql > select '170325171202362941'+0.0;
+--------------------------+
| '170325171202362941'+0.0 |
+--------------------------+
| 1.7032517120236294e17 |
+--------------------------+
1 row in set (0.00 sec)
字符串'170325171202362931'
和'170325171202362941'
转化为浮点型结果一样,我们看下他们和数值的比较结果
mysql > select '170325171202362931' = 170325171202362930;
+-------------------------------------------+
| '170325171202362931' = 170325171202362930 |
+-------------------------------------------+
| 1 |
+-------------------------------------------+
1 row in set (0.00 sec)
mysql > select '170325171202362941' = 170325171202362930;
+-------------------------------------------+
| '170325171202362941' = 170325171202362930 |
+-------------------------------------------+
| 1 |
+-------------------------------------------+
1 row in set (0.00 sec)
结果也是符合预期的。
因此,当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,一定要注意字段类型,杜绝隐式转化,不仅会导致查询缓慢,还会导致结果错误。
来源 | https://urlify.cn/mMBFZz
- SendCloud邮件队列状态和已使用额度的Python监控脚本
- linux/scp命令报“bash: scp: command not found lost connection”错误的解决办法
- bat/cmd批处理连接SqlServer数据库查询脚本
- 一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎
- 解决mstsc无法连接问题:由于没有远程桌面授权服务器可以提供许可证…
- Apache/Nginx伪静态规则匹配http://出现的问题与解决
- 微信文件微起底
- Go语言TCP Socket编程--1
- Go语言TCP Socket编程--2
- 服务器 数据库设计技巧--1
- CVE-2015-0235:Linux glibc高危漏洞的检测及修复方法
- zabbix监控在lnmp环境下编译安装小记
- 【重磅】百度开源分布式深度学习平台,挑战TensorFlow (教程)
- WordPress评论ajax动态加载,解决静态缓存下评论不更新问题
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- Lambda表达式
- java_字节流、字符流
- HashMap源码分析
- [周末往期回顾]使用cx_Oracle连接Oracle
- java_数组的定义与操作
- LinkedList源码分析
- MarkDown语法的详细使用教程
- java_内部类、匿名内部类的使用
- [周末往期回顾]使用BBED定位数据位置
- JeecgCloud版,新建项目。
- MySQL忘记root密码,错误号码1045解决办法
- java_流程控制语句、权限修饰符
- [Oracle 故障处理]记一次RMAN备份警告的处理过程
- [Oracle 日常管理]使用oradebug捕获SQL语句
- [Oracle 日常管理]ERRORSTACK使用介绍