mysql数据库UPDATE语句一个bug分析

这个我认为的bug,反馈给MySQL官方,但是MySQL官方认为这并不是一个bug,并给出了解释,我认为这个解释是合理的,但是不可避免的是这条语句实在太危险了。

问题描述

示例表结构与表数据:

# 表结构
mysql> show create table t;
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| t | CREATE TABLE `t` (
 `id` int(11) DEFAULT NULL,
 `c1` int(11) DEFAULT NULL,
 `c2` varchar(16) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

# 表数据
mysql> select * from t;
+------+------+------+
| id | c1 | c2 |
+------+------+------+
| 1 | 1 | A |
| 2 | 2 | B |
| 3 | 3 | C |
+------+------+------+
3 rows in set (0.00 sec)
正常的UPDATE语句应该长这样子的,SET后接的并列字段分隔符为”逗号(,)”:

mysql> select * from t;
+------+------+------+
| id | c1 | c2 |
+------+------+------+
| 1 | 1 | A |
| 2 | 2 | B |
| 3 | 3 | C |
+------+------+------+
3 rows in set (0.00 sec)

mysql> update t set c1=11,c2='AA' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from t;
+------+------+------+
| id | c1 | c2 |
+------+------+------+
| 1 | 11 | AA |
| 2 | 2 | B |
| 3 | 3 | C |
+------+------+------+
3 rows in set (0.00 sec)
不合理的UPDATE应该是什么样子的呢,是将SET后接的并列字段分隔符改为”AND”, 注意这样写的话,MySQL并不会报错,还会执行成功,但是语义完全和”逗号”作为分隔符是两码事 :

# 先来个实验
mysql> select * from t;
+------+------+------+
| id | c1 | c2 |
+------+------+------+
| 1 | 1 | A |
| 2 | 2 | B |
| 3 | 3 | C |
+------+------+------+
3 rows in set (0.00 sec)

mysql> update t set c1=11 and c2='AA' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from t;
+------+------+------+
| id | c1 | c2 |
+------+------+------+
| 1 | 0 | A |
| 2 | 2 | B |
| 3 | 3 | C |
+------+------+------+
3 rows in set (0.00 sec)
# 实验走到这里,觉得这个update不是我想要的,但是这个c1咋变为0了呢,c2咋没变呢?

# 再来个实验
mysql> select * from t;
+------+------+------+
| id | c1 | c2 |
+------+------+------+
| 1 | 1 | A |
| 2 | 2 | B |
| 3 | 3 | C |
+------+------+------+
3 rows in set (0.00 sec)

mysql> update t set c2='AA' and c1= 11 where id=1;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 1

mysql> select * from t;
+------+------+------+
| id | c1 | c2 |
+------+------+------+
| 1 | 1 | 0 |
| 2 | 2 | B |
| 3 | 3 | C |
+------+------+------+
3 rows in set (0.00 sec)
# 咦?这里c2变了,咋变为了0呢?我明明是个字母啊,再怎么改也不能改成一个字符串型数字啊?c2咋依旧没变呢?
问题原因

原来在UPDATE … SET后接分隔符为”AND”的语句,会处理成逻辑运算true(1) / false(0),直接上实验好了==

# 第一个实验解析
update t set c1=11 and c2='AA' where id=1;
# 在这条语句中MySQL将c1=11 and c2='AA'解析成了c1=(11 and c2='AA'),而在这张表中(11 and c2='AA')是一个假语句(false),所以MySQL将c1值解析为c1=0
mysql> select 11 and c2='AA' from t where id=1;
+----------------+
| 11 and c2='AA' |
+----------------+
| 0 |
+----------------+
1 row in set (0.00 sec)
# 所以最终出现了c1变为0,c2没有任何改变的现象

# 第二个实验解析
update t set c2='AA' and c1= 11 where id=1;
# 在这条语句中MySQL将c2='AA' and c1= 11解析成了c2=('AA' and c1= 11),而在这张表中('AA' and c1= 11)是一个假语句(false),所以MySQL将c2值解析为c2=0,然后隐式转换,将'0'存储到c2列
mysql> select 'AA' and c1= 11 from t where id=1;
+-----------------+
| 'AA' and c1= 11 |
+-----------------+
| 0 |
+-----------------+
1 row in set, 1 warning (0.01 sec)
# 所以最终出现了c2变为'0',c1依旧没变

问题总结

这个问题告诉我们,SQL语句一定要写规范了,执行SQL语句前一定要清楚这条SQL的运行逻辑,对于UPDATE/DELETE手动执行之前,一定要按照如下顺序来==

begin;
update / delete ...;
select 校验数据;
commit; --数据校验成功
rollback; --数据校验失败

时间: 2024-09-19 09:25:29

mysql数据库UPDATE语句一个bug分析的相关文章

MySQL中UPDATE语句使用的实例教程_Mysql

一.UPDATE常见用法首先建立测试环境:   DROP TABLE IF EXISTS t_test; CREATE TABLE t_test ( bs bigint(20) NOT NULL auto_increment, username varchar(20) NOT NULL, password varchar(20) default NULL, remark varchar(200) default NULL, PRIMARY KEY (bs) ) ENGINE=InnoDB AUT

mysql数据库如何设置一个字段不重复,一个字段自增?

问题描述 mysql数据库如何设置一个字段不重复,一个字段自增? 如题. mysql数据库如何设置一个字段不重复,一个字段自增? 解决方案 CREATE TABLE t_user (Id int(11) NOT NULL AUTO_INCREMENT, -- 自增username varchar(18) NOT NULL unique, -- 唯一性约束password varchar(18) NOT NULL, PRIMARY KEY (Id) ) ENGINE=InnoDB AUTO_INC

php+MySQL判断update语句是否执行成功的方法_php技巧

update语句是PHP+MySQL中常用的操作,判断update语句是否执行成功是其中非常重要的一个环节.本文就以实例展示了php+MySQL判断update语句是否执行成功的方法.分享给大家供大家参考之用.具体方法如下: 代码一: $rs=MySQL_query($sql); if(mysql_affected_rows()) echo "sql执行成功"; else echo "sql执行失败"; 代码二: <?php /* 连接数据库 */ mysql

MySQL数据库SQL语句的C++ ODBC接口类测试结果 (转载)

c++|mysql|odbc|数据|数据库|语句 发信人: engineer (剑胆琴心~还是得走,sigh...), 信区: Linux 标  题: MySQL数据库SQL语句的C++ ODBC接口类测试结果 (转载) 发信站: BBS 水木清华站 (Mon Aug  9 18:03:47 1999)   [ 以下文字转载自 Database 讨论区 ] [ 原文由 engineer 所发表 ]         MySql数据库SQL语句的C++ ODBC接口类测试结果            

mysql-初学JDBC连接MYSQL数据库,出现一个异常,求教,在线等

问题描述 初学JDBC连接MYSQL数据库,出现一个异常,求教,在线等 首先贴下拿来做试验的数据库,证明我数据库名没写错?d-7test 用户名,密码也没错耶 再来贴下代码: jar包也是加了的: 最后红条,报了这个错: 解决方案 Classpath中有没有包含你的mtsql驱动jar包? 解决方案二: getConnection里头不要写"url:"啊!,直接jdbc:mysql.... 解决方案三: 你的connect配置不对 解决方案四: 检查路径下有没有mysql-jdbc的j

python mysql数据库插入语句问题

问题描述 python mysql数据库插入语句问题 本人新手,想在python中实现数据库插入,但是只要参数有中文字符就执行不了,换成英文字符没有出错 解决方案 数据库连接用utf8编码,插入的中文等也用utf8编码,然后再插入数据库 解决方案二: 楼上说的对,在文件开头写入#-*- coding:utf-8 -*- 就好了

sql优化-mysql数据库sql语句优化,求大神!!!!

问题描述 mysql数据库sql语句优化,求大神!!!! SELECT DISTINCT uid, level,username,ansnum FROM test WHERE level=100 GROUP BY uid ORDER BY ansnum DESC LIMIT 12; uid.ansnum均已建索引,主要是GROUP BY uid导致特别慢,如何提速??? 解决方案 MySQL数据库SQL语句优化原则 解决方案二: 根据你的查询需求,没有特别好的优化办法.注意group by 和o

命令行模式下备份、还原 MySQL 数据库的语句小结_Mysql

为了安全起见,需要经常对数据库作备份,或者还原.对于 MySQL 而言,最方便的方法可能就是用 phpMyAdmin 的导出.导入功能了,但如果你的数据库体积比较大,作为 Web 应用的 phpMyAdmin 可能会遭遇"超时"而操作失败.所以,学会在命令行模式下备份.还原数据库,还是很有必要的. 1.备份数据库 在 Linux 命令行模式下备份 MySQL 数据库,用的是 mysqldump 命令: 复制代码 代码如下: mysqldump -u mysqluser -p test_

mysql数据库更新语句一次最多能更新多少条信息?有没有总的大小限制?

问题描述 如题:请问给位老师,MySQL数据库的update语句一次更新数据多少条?有没有限制大小? 解决方案 解决方案二:使用的过程中也没有给过很大的数据量,都能update成功.没试过极限情况,这个应该没有数量限制吧.解决方案三:自己可以试试嘛,用循环插入上百万条记录,然后updata下,你就知道了我是没试过--