当前位置: 首页 > 科技观察

小心把我搞砸的MySQL双引号!

时间:2023-03-15 20:11:40 科技观察

1.前言最近经常遇到开发者误删除、误更新数据。这不,他们又找我麻烦了。来看看整个过程,看得我好惨。2、流程因为开发需要修复生产环节的数据,需要执行120条SQL语句,需要更新数据,所以开发连接生产数据库,先执行第一条SQL:updatetablenamesetsource_name="bj1062-BeijingChaoyangDistrictChangyingBeichenfudi"wheresource_name="-ChangyingBeichenfudi,ChaoyangDistrict,Beijing"我们仔细看了一下这条SQL,确实没有问题,where条件也正常。大意是在这个地址前面加上字符字符串bj1062,真的没有报错吗?是的,没有错误。开发执行完成后,结果确实符合预期。然后开发执行剩下的SQL,和上面的SQL一样,更新地址。执行完之后,开发一头雾水,发现source_name变成了0,开发赶紧给我打电话说:哈维,我执行了update,whereconditions是正确的,set的值也是正确的,但是之后set所有字段都变成0了,请速速帮我看看能不能恢复数据。赶紧上了服务器,这期间查看了binlog,发现有大量updatetablenamesetsource_name=0语句,使用binlog2sql进行分析。项目地址:binlog2sqlhttps://github.com/danfengcao/binlog2sql开发确定运行时间点,生成闪回SQL,进行数据恢复,同时保留现场证据。然后查看了开发执行的SQL,发现了一些很奇怪的SQL。这些SQL的引号跑到了where字段名的后面,简化后的SQL变成了:updatebl_namesetstr_col="xxx"="yyy"那么这条SQL在MySQL中是如何进行语义转换的呢?会不会是这样的?updatebl_nameset(str_col="xxx")="yyy"语法错误,所以只会是下面的形式,updatebl_namesetstr_col=("xxx"="yyy")select"xxx"="yyy"的值为0,所以updatebl_namesetstr_col="xxx"="yyy"相当于updatetbl_namesetstr_col=0,导致source_name字段全部更新为0。我们研究一下select表单中的语句会发生什么情况。mysql[localhost]{msandbox}(test)>selectid,str_colfromtbl_namewherestr_col="xxx"="yyy";+----+-----+|id|str_col|+----+---------+|1|aaa||2|aaa||3|aaa||4|aaa|+----+--------+我们发现这条SQL也找到了str_col='aaa'的记录,为什么?mysql[localhost]{msandbox}(test)>warningsShowwarningsenabled.mysql[localhost]{msandbox}(test)>explainextendedselectid,str_colfromtbl_namewherestr_col="xxx"="yyy"\G*****************************1.row******************************id:1select_type:SIMPLEtable:tbl_nametype:indexpossible_keys:NULLkey:idx_strkey_len:33ref:NULLrows:4filtered:100.00Extra:Usingwhere;Usingindex1rowinset,1warning(0.00sec)Note(Code1003):/*select#1*/select`test`.`tbl_name`.`id`AS`id`,`test`.`tbl_name`.`str_col`AS`str_col`from`test`.`tbl_name`where((`test`.`tbl_name`.`str_col`='xxx')='yyy')这里他把where条件转化为((`test`.`tbl_name`.`str_col`='xxx')='yyy')这个条件先判断str_col和'xxx'是否相等,如果相等,则里面括号的值为1,不相等则为0再0或1然后用'yyy'判断,因为等号的那一边在t,另一边是字符串,两边都转成浮点数进行比较。可以看我之前的文章MySQL隐式转换导致查询结果错误的案例分析。'yyy'转为浮点型为0、0、0。比较等于1mysql[localhost]{msandbox}(test)>select'yyy'+0.0;+----------+|'yyy'+0.0|+------------+|0|+----------+1rowinset,1warning(0.00sec)mysql[localhost]{msandbox}(test)>select00=0;+-----+|00=0|+-----+|1|+-----+1rowinset(0.00sec)导致不断建立结果,即select语句相当于如下SQL:selectid,str_colfromtbl_namewhere11=1;查询所有记录。3.总结在写SQL的过程中,一定要注意引号的位置是否正确。有时候引号的位置错了,SQL还是正常的,但是会导致所有的执行结果都不对。执行前必须在测试环境中执行测试,结合IDE的语法高亮可以发现相应的问题。