萬盛學電腦網

 萬盛學電腦網 >> 數據庫 >> mysql教程 >> mysql數據庫UPDATE語句一個bug分析

mysql數據庫UPDATE語句一個bug分析

下面我們來看一篇關於mysql數據庫UPDATE語句一個bug分析,希望這篇文章能夠幫助到各位理解到mysql數據庫UPDATE語句例子。


這個我認為的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; --數據校驗失敗

copyright © 萬盛學電腦網 all rights reserved