今天Update又被雙引號坑了
一,前言
最近經(jīng)常碰到開發(fā)誤刪除刪除更新數(shù)據(jù),這不,他們又給我找了個麻煩,我們來看下整個過程,把我坑得夠慘。
二,過程
由于開發(fā)需要在生產(chǎn)連續(xù)中修復(fù)數(shù)據(jù),需要執(zhí)行120條SQL語句,需要將數(shù)據(jù)進行更新,于是開發(fā)連上了生產(chǎn)數(shù)據(jù)庫,首先執(zhí)行了第一條SQL
更新 ?表名? 集 ?source_name =? “ bj1062-北京朝陽區(qū)常營北辰福第”??
其中?source_name =????? “-北京市朝陽區(qū)常營北辰福第”
我們仔細看了下,這個SQL,的確沒有什么問題,其中條件也是正常的,大意就是將這個地址的前面加字符串bj1062,是真的沒有錯誤么?是的沒有錯誤。開發(fā)執(zhí)行完成后,結(jié)果的確是符合預(yù)期。
然后開發(fā)執(zhí)行了剩下的SQL,都是和上面的SQL一樣,將地址進行更新。執(zhí)行完成后,開發(fā)懵逼了,發(fā)現(xiàn)source_name都變成了0,開發(fā)趕緊給我打電話說:
Harvey,我執(zhí)行了更新,其中條件都是對的,set的值也是對的,但是set后的部分全部都變成了0,你趕緊幫我看看,看看能不能恢復(fù)數(shù)據(jù)。
我趕緊登上服務(wù)器,查看了這段時間的binlog,發(fā)現(xiàn)縮短的更新表名set source_name = 0的語句,利用binlog2sql進行了解析

趕緊和開發(fā)確定了操作的時間點,生成閃回的SQL,進行了數(shù)據(jù)恢復(fù),同時保留了現(xiàn)場證據(jù)。
然后對開發(fā)執(zhí)行的SQL進行了檢查,發(fā)現(xiàn)了幾條很詭異的SQL

這幾條SQL的引號位置跑到了where相鄰名稱后面,簡化后的SQL變成了:
更新 ?tbl_name? set ?str_col = “ xxx” ?=? “ yyy”
那么這個SQL在MySQL他是如何進行語義轉(zhuǎn)換的呢?
可能是下面這樣的么?
更新 ?tbl_name? 設(shè)置 ?(str_col = “ xxx” ?)=? “ yyy”
這樣就語法錯誤了,那么只會是下面這樣的形式,
更新 ?tbl_name? 設(shè)置 ?str_col =(“ xxx” ?=? “ yyy”)
而
選擇?“ xxx” ?=? “ yyy”?
的值是0,所以
更新 ?tbl_name? set ?str_col = “ xxx” ?=? “ yyy”
等價于
更新 ?tbl_name? 設(shè)置 ?str_col = 0
所以就導(dǎo)致了source_name擴展全部更新了0。
我們再研究下選擇形式這種語句會怎么樣。
mysql [localhost] {msandbox}(測試)>從tbl_name選擇id,str_col,其中str_col = “ xxx” ?=? “ yyy” ;
+ ---- + --------- +
| id | ?str_col? |
+ ---- + --------- +
| ??1 ?| aaa |
| 2 | ?aaa????? |
| ??3 ?| aaa |
| 4 | ?aaa????? |
+ ---- + --------- +
我們發(fā)現(xiàn),這個SQL將str_col ='aaa'的記錄也查找出來了,為什么呢?
mysql [localhost] {msandbox}(測試)> warnings
顯示警告已啟用。
mysql [localhost] {msandbox}(測試)>解釋擴展選擇ID,str_col from tbl_ name,其中str_col =“ xxx” =“ yyy” \ G
***************** ********** 1.行***************************
?????????? ID:1
? select_type:SIMPLE
????????表:tbl_name
?????????類型:索引
可能的
??????????鍵:NULL 鍵:idx_str
????? key_len:33
??????????引用:NULL
?????????行:4已
?????過濾:100.00
????????額外:使用where;
在集合中使用索引1行,警告1個(0.00秒)
注意(代碼1003):/ * select#1 * /選擇? `test`。`tbl_name`。`id` ?AS? `id`,`test`。`tbl_name`。`str_col` ?作為? ?來自? `test`的`str_col`。`tbl_name` ?其中((`test`。`tbl_name`。`str_col` ?= 'xxx'的)= 'YYY')
這里他把哪里條件轉(zhuǎn)化變成
((`test`。`tbl_name`。`str_col` ?=? 'xxx'的)=? 'YYY' )
這個條件的初步判斷str_col和'xxx'是否一致,如果替代,那么里面括號的比例1,如果不對應(yīng),就是0
然后0或1再和和'yyy'進行判斷,由于等號一邊是int,另外一邊是字符串,兩邊都轉(zhuǎn)換為float進行比較,'yyy'轉(zhuǎn)換為浮點型為0,0和0比較恒等于1
mysql [localhost] {msandbox}(測試)>? 選擇?'yyy' + 0.0 ;
+ ----------- +
|?'yyy' + 0.0 ?|
+ ----------- +
|?????????0 ?|
+ ----------- +
1 ?行? 中 ?集合,? 1個 ?警告(0.00 ?秒)
的MySQL [本地主機] {msandbox}(試驗)>? 選擇?0 = 0 ;
+ ----- +
|?0 = 0 ?|
+ ----- +
|???1 ?|
+ ----- +
1 ?行? 中 ?集(0.00 ?秒)
這樣導(dǎo)致結(jié)果恒成立,也就是select語句等價于以下SQL
?從 ?tbl_name? 中選擇id,str_col? ,其中?1 = 1 ;
將查詢出所有的記錄。
三,小結(jié)
在寫SQL的過程中,一定要小心引號的位置是否正確,有時候引號位置錯誤,SQL依然是正常的,但是卻會導(dǎo)致執(zhí)行結(jié)果全部錯誤。在執(zhí)行前必須在測試環(huán)境執(zhí)行測試,結(jié)合IDE的語法高亮發(fā)現(xiàn)相應(yīng)的問題。
來源:對于DB?A
www.fordba.com/mysql-double-quotation-marks-accident.html
——————END——————
歡迎關(guān)注“Java引導(dǎo)者”,我們分享最有價值的Java的干貨文章,助力您成為有思想的Java開發(fā)工程師!
