当前位置: 首页 > 后端技术 > Java

同事写更新,误用双引号,生产数据全部改0!

时间:2023-04-01 13:49:53 Java

1.前言最近经常遇到开发者不小心误删、误更新数据。这不,他们又找我麻烦了。让我们来看看整个过程。2.流程因为开发需要修复生产环节的数据,需要执行120条SQL语句,需要更新数据,所以开发连接生产数据库,先sqlupdatetablenamesetsource_name="bj1062-北京朝阳先执行。长影区北辰府第"wheresource_name="-北京朝阳区长影北辰府第"我们仔细看了一下这条SQL,确实没有问题,where条件是也正常。大意是加上字符串bj1062真的没有错误吗?是的,没有错误。开发执行完成后,结果确实符合预期。然后开发执行剩下的SQL,和上面的SQL一样,更新地址。执行完成后,开发一头雾水,发现source_name变成了0,开发赶紧打电话给我说:我赶紧上了服务器,这期间查看了binlog,发现大量updatetablenamesetsource_name=0该语句使用binlog2sql进行分析,项目地址:binlog2sqlsourcewei01随开发快速确定运行时间点,生成闪回SQL,进行数据恢复,同时保留现场证据。然后查看开发执行的SQL,发现有几个很奇怪的SQL:这些SQL的引号跑在了where字段名的后面,简化后的SQL变成了:updatetbl_namesetstr_col="xxx"="yyy"那么如何这个SQL在MySQL中执行语义转换?会不会是这样的?updatetbl_nameset(str_col="xxx")="yyy"是语法错误,所以只会是下面的形式,updatetbl_namesetstr_col=("xxx"="yyy")andselect"xxx"="yyy”的值为0,所以updatetbl_namesetstr_col="xxx"="yyy"等价于updatetbl_namesetstr_col=0,导致所有source_name字段都更新为0。我们来研究一下形式的语句选择将要发生的事情。mysql[localhost]{msandbox}(test)>selectid,str_colfromtbl_namewherestr_col="xxx"="yyy";idstr_col1aaa2aaa3aaaaa_col=SQL会把记录也查出来,为什么?mysql[localhost]{msandbox}(test)>warningsShowwarningsenabled.mysql[localhost]{msandbox}(test)>explainextendedselectid,str_colfromtbl_namewherestr_col="xxx"="yyy"\G1.行**ID:1select_type:简单表:tbl_name类型:indexpossible_keys:null键:idx_strkey_len:33ref:nullcod:null行:4过滤:100.00extraextra:在集合中使用索引1行,1警告(0.00秒)注意(代码1003):/select#1/selecttest.tbl_name.idASid,test.tbl_name.str_colASstr_colfromtest.tbl_namewhere((test.tbl_name.str_col='xxx')='yyy')这里他把where条件转化为((test.tbl_name.str_col='xxx')='yyy')。这个条件先判断str_col和'xxx'是否相等。如果相等则里面括号的值为1,如果不相等则为0再0或1再用'yyy'判断,由于等号的一边是int,另一边是string,所以两边都转成float进行比较。可以看我之前的文章mysql'yyy'隐式转换导致查询结果错误的案例分析转换为浮点数0,0和0等于1mysql[localhost]{msandbox}(test)>select'yyy'+0.0;'yyy'+0.00集合中有1行,1条警告(0.00秒)mysql[localhost]{msandbox}(测试)>选择0=0;0=011rowinset(0.00sec)这样结果总是成立的,即select语句相当于下面的SQLselectid,str_colfromtbl_namewhere1=1;会查询3.总结在写SQL的过程中,一定要注意引号的位置是否正确。有时候引号的位置错了,SQL还是正常的,但是会导致所有的执行结果都不对。执行前必须在测试环境中执行测试,结合IDE的语法高亮可以发现相应的问题。资料来源:fordba.com/mysql-double-quotation-marks-accident.html