【问题标题】:Is this SQL code susceptible to rounding errors?此 SQL 代码是否容易出现舍入错误?
【发布时间】:2009-09-25 18:30:01
【问题描述】:

我发现了一些奇怪的行为,即存储过程返回一两美分的不准确结果。

代码如下(不是我写的):

ALTER  PROCEDURE [dbo].[TN_GetSimpleBalance] 
    @custID int,
    @nBalance decimal (8,2) output
AS

declare @ArBalance as decimal (8,2)
declare @custStatusCode varchar (2)
declare @unbilledCallsAmount as decimal (8,2)

set @nBalance = 0

set @ArBalance = 0
set @custstatusCode = ''
set @unbilledCallsAmount = 0


SET NOCOUNT ON 

    select @unbilledCallsAmount = isnull(sum(callcharge+taxamount),0) 
    from call with (NOLOCK) where custid = @custID and callstatuscode in ('R', 'B')

    --get AR balance
    select @ArBalance = isnull(sum(amount),0) 
    from artran with (NOLOCK) 
    where custid = @custID AND POSTEDFLAG ='Y'

    set @nBalance = @unbilledCallsAmount + @ArBalance

@nBalance 显示为零,即使另一个应用程序告诉我客户有 0.02 美元。 callchargetaxamount 都是货币数据类型。

这是我第一次遇到这种情况,但我正在将一些相关代码转移到生产环境中,并被“要求”对此进行研究。

你怎么看?金钱和十进制数据类型之间有什么奇怪的地方吗?您认为还有什么可以解释这一点的吗?

【问题讨论】:

  • 我唯一不确定的是数量数据类型。您可以尝试将货币字段转换为十进制 8,2 并查看结果是否改变
  • 所以 this 是 Michael Bolton 的办公空间代码...
  • 约翰 - 我也是这么想的 =)

标签: sql-server decimal rounding currency


【解决方案1】:

我在您的代码中没有看到任何货币数据类型,但我认为 call.callcharge 和 call.taxamount 是?我不知道精度问题。

您能做的最好的事情可能是尝试找到数据存在这种差异的特定测试用例,看看您是否可以一致地重现它。然后,您可以一次提取一条语句的逻辑,并找出差异是在哪里引入的。

您的通话费或税额是否可能为零? (null+(-$0.02)) => 空;也许

isnull(sum(callcharge+taxamount),0) 

应该是:

sum(isnull(callcharge, 0)+isnull(taxamount,0))

@unbilledCallsAmount 或 @ArBalance 如果这些选择语句不返回任何记录,也可能为 null。 高温,

【讨论】:

    【解决方案2】:

    callchargetaxamount 是什么数据类型?我会在该数据类型中进行所有计算,并在最后转换为十进制。目前,您所有的中间变量都是小数,这意味着舍入误差复合。不要圆到最后。

    【讨论】:

    • callcharge 和 taxamount 都是货币数据类型。
    【解决方案3】:

    当使用这个,并尝试不同的硬编码值 callcharge 和 taxamount 时:

    declare @DecimalAmount as decimal (8,2)
    declare @MoneyAmount as money
    
    select @DecimalAmount = isnull(sum(callcharge+taxamount),0) 
          ,@MoneyAmount   = isnull(sum(callcharge+taxamount),0) 
        from (select CONVERT(money,1.43) AS callcharge, CONVERT(money,.83) AS taxamount
              UNION select CONVERT(money,1.43) AS callcharge, CONVERT(money,.01) AS taxamount
              UNION select CONVERT(money,1.43) AS callcharge, CONVERT(money,.99) AS taxamount
              UNION select CONVERT(money,1.43) AS callcharge, CONVERT(money,.03) AS taxamount
             ) dt
    
    select @DecimalAmount,@MoneyAmount 
    

    如果您的通话费或税额有 3 位或更多小数位,我只能得到不同的值。因此,如果您没有这样的数据,那也没关系。

    【讨论】:

      【解决方案4】:

      由于datatype precedence,您的钱(来自评论)列正在转换为十进制(8,2)。而money-> numeric (near end) 是四舍五入的,它们是四舍五入的

      所以@unbilledCallsAmount 和@ArBalance 都将被四舍五入,并且都是累积的。

      【讨论】:

        猜你喜欢
        • 2011-12-22
        • 1970-01-01
        • 1970-01-01
        • 2010-12-20
        • 1970-01-01
        • 2014-02-23
        • 2012-11-16
        • 2019-09-22
        • 2021-03-19
        相关资源
        最近更新 更多