我们有个电子商务的系统,商家(也就是给我们下单的用户)在我们系统中预存款来买东西,每个商家一天的订单量大约一千多条。商家、订单都存储在数据库中,商家的余额和订单金额字段类型为:decimal(18, 4)。商家余额的变化只有两种情况:随着订单的完成减去金额,为账户加款。昨天查账,发现在系统存在一个问题,当天0点某商家的余额减去当天该商家的总订单金额,再加上该商家的加款,与当天该商家的余额相比差了5分钱。我挨个查了一下订单,发现问题出在某个时间段先给该商家余额加了20000.16元(增加金额以后余额是正常的),然后该商家买了24.69元的商品后少扣了5分钱。我又去查了出现同样情况的其它商家,发现问题也是这样产生的,都是先给这个商家增加了余额,然后该商家再买产品扣除订单金额的时候会出现很小的误差(大概就是几分钱)。虽然钱不多,但却能反映出系统安全性和数据准确性的问题。在系统中,数据库用的是SQL Server 2000,存取数据库是用Java实现的,在Java中凡是和资金打交道一律使用BigDecimal类型(能够保证计算时没有任何浮点数运算误差和精度损失)。事后我简单地测了一下加款的功能,也没什么问题。商户是通过程序向我们接口自动提交订单的,同一商户可以并发提交订单。我查了下发生问题的那个订单,该订单在时间上和其它订单、加款操作没有重叠,可以看做顺序发生的。我现在对这个问题是摸不着头脑,不知道问题出在哪里,感觉如果是程序问题的话,比如少扣款了,出现的误差就不只区区几分钱了。请各位有经验的朋友帮忙分析下问题可能出现在哪里?

解决方案 »

  1.   

    1。 首先程序debug跟踪下,传入的参数是否如自己预想
    2。 排除传入参数的问题,sql2000在 decimal运算时,热别是乘/除时,要小心小数位
      

  2.   

    数据类型是怎么样的??如果是float本来就不精确
      

  3.   


    我在数据库中用的是decimal,不知道这个类型的浮点数计算会不会有误差,我在程序里用的是Java的BigDecimal,这个不会出问题。
      

  4.   

    这个问题我很早以前碰到过,就是每发生一笔都要四舍五入到分上面去,decimal(18, 4)保留四位小数,这样会存在明细中比如两个0.0030加起来四舍五入到分上是一分钱,而单个0.0030元四舍五入到分上是0元,数据多了就会存在差积分前的问题。
      

  5.   


    不会吧,我对SQL Server不是很熟,我用的不是货币类型,数据库当然不知道我会四舍五入到分上去,我设的小数点是4位,它应该要保证我运算的准确性吧,怎么能随便就四舍五入了,如果我加一个小数后五位的数倒是会这样,但系统控制要增减的金额都是小数点不超过4位的。另外出现这个问题并不是累加损失精度的结果,而是一次数据库操作造成的。
      

  6.   

    问题查出来了。在加款的时候,更新余额的SQL语句是这样写的:
    UPDATE CUSTOMER SET BALANCE = BALANCE + ? WHERE CUSTOMER_ID=?
    ?是占位符,可以设置参数,金额参数是BigDecimal类型的变量,存放的是小数位有2位的金额,然后执行SQL语句会发现数据库中存放的结果小数点后第3、4位都四舍五入处理了,但是我直接在分析查询器里执行同样的SQL就没有问题。现在怀疑是微软提供的JDBC驱动的问题。