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

一个update语句引起的意外,这次让开发长记性了!

时间:2023-03-18 16:54:34 科技观察

1.前言最近经常遇到开发者不小心误删、误更新数据。这不,他们又找我麻烦了。让我们来看看整个过程。2、流程因为开发需要修复生产环节的数据,需要执行120条SQL语句,需要更新数据,所以开发连接生产数据库,先执行第一条SQL:updatetablenamesetsource_name="bj1062-BeijingChaoyangDistrictChangyingBeichenfudi"wheresource_name="-ChangyingBeichenfudi,ChaoyangDistrict,Beijing"我们仔细看了一下,发现这条SQL没有问题,where条件也正常。大意是在bj1062这个地址前面加上一个字符串,真的没有错误吗?是的,没有错误。开发执行完成后,结果确实符合预期。然后开发执行剩下的SQL,和上面的SQL一样,更新地址。执行完之后,开发一头雾水,发现source_name变成了0,开发赶紧打电话给我说:哈维,我执行了update,where条件是对的,set的值也是对的,但是设置后的值所有字段都变成0了,请速速帮我看看能不能恢复数据。赶紧上了服务器,这期间查看了binlog,发现有大量updatetablenamesetsource_name=0语句,使用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`.`s这里他把where条件转换成((`test`.`tbl_name`.`str_col`='xxx')='yyy')的这个条件首先判断str_col和'xxx'是否相等。如果相等则里面括号的值为1,不相等则为0再0或1再用'yyy'判断,因为等号的一边是int,另一边是a字符串,两边都转为float进行比较,可以看我之前的文章mysql隐式转换导致查询结果错误案例分析'yyy'转为浮点型为0,0和0等于1.mysql[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语句等价于下面的SQLselectid,str_colfromtbl_namewhere11=1;将查询所有记录。3.总结在写SQL的过程中,一定要注意引号的位置是否正确。有时候引号的位置错了,SQL还是正常的,但是会导致所有的执行结果都不对。执行前必须在测试环境中执行测试,结合IDE的语法高亮可以发现相应的问题。