先说结论和教训:
尽量避免在sql中使用不同类型的值进行操作,不然在程序运行中很有可能出现很多奇奇怪怪的问题就像以下sql,执行结果看上去会难以理解
以下是正文:
(不想看我啰嗦可以跳到分析)
某天,运营人员反馈说有一笔订单客户取消后没有生成售后单,通过管理台手工触发生成售后单一直报“售后单已存在”,让技术这边帮忙看看什么原因。
于是我登录了管理台查看了该订单,该订单客户已经付过款,付款后该订单拆单生成了两笔子订单,客户取消订单后其中一笔子订单已经生成了售后单,另外一笔子订单没有对应的售后单,手工触发生成售后单,返回信息说“售后单已存在”。
至此,有了明确的信息,打开开发工具,订位到代码返回信息的位置,分析了对应的逻辑,逻辑大概就是在生成售后单前,查询一遍数据库中是否存在该子订单的售后单,有对应售后单则不进行重复生成,使用的sql如下
select count(1) from 售后表 where order_id = #{orderId}
于是我连上生产的数据库(小公司没有DBA。。。)执行了以下sql
其中引号内的字符串为没有生成售后单对应子订单的主键。
到这我感觉有点迷,这不是没有查到吗,怎么会报“订单已存在”呢?
于是我只能去查看生产的日志了,最总找到的日志中,执行的sql长这样:
select count(1) from 售后表 where order_id = ?
入参是(Long)1149156865679032322
看上去也没啥问题毕竟值都对上了,到这里我没有意识到带引号和不带引号会使结果天差地别,毕竟订单表的主键确实使用的是Long类型,这里传入的参数也是Long类型,于是走了很多岔路去排查问题,浪费了很多时间,最后直到我执行了以下sql
What for ?!!!
我查询的明明是22结尾,怎么给我查询出来23结尾的数据,我用的是等号啊!
直到后来,我看到了售后表的表结构,order_id居然变成了varchar类型。。。。
至此,这个问题的来龙去脉我算是搞清楚了
分析:
订单表的主键是Long类型,售后单表存储订单表主键的字段order_id是varchar类型,在生成售后单判断售后单是否已存在的逻辑中,执行的sql传入Long类型的order_id去查询varchar类型的售后单表中订单主键,sql的where条件等号左边是varchar,右边是数字,进行了隐式类型转换,导致查询结果有误,查询到另一子订单的售后单记录,程序判断订单对应售后单已存在,报“订单已存在”,且没有继续生成售后单。
重要的事说三遍:
MySql执行sql切记不要让sql进行类型转换!!!
MySql执行sql切记不要让sql进行类型转换!!!
MySql执行sql切记不要让sql进行类型转换!!!
以上