此 SQL 代码是否容易出现舍入错误?

发布于 2024-08-05 15:33:57 字数 1000 浏览 7 评论 0原文

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

这是代码(不是我写的):

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 都是货币数据类型。

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

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

I have discovered some strange behavior where a stored procedure is returning inaccurate results by a penny or two.

Here's the code (I didn't write it):

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 is showing zero, even though another app is telling me the customer has $.02. callcharge and taxamount are both money datatypes.

This is the first time I've encountered this condition, but I am moving some related code to production and have been "asked" to research this.

What's your take? Is there weirdness going between money and decimal datatypes? Anything else you think might explain this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

总以为 2024-08-12 15:33:57

我在您的代码中没有看到任何金钱数据类型,但我假设 call.callcharge & call.taxamount 是多少?我暂时不知道精度问题。

也许您能做的最好的事情就是尝试找到数据存在这种差异的特定测试用例,并看看是否可以一致地重现它。然后,您可以一次将逻辑分解为一个语句,并找出差异所在。

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

isnull(sum(callcharge+taxamount),0) 

应该是:

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

如果这些 select 语句不返回任何记录,@unbilledCallsAmount 或 @ArBalance 也可能为 null。
哈特哈,

I don't see any money datatypes in your code, but I assume call.callcharge & call.taxamount are? I don't know about the precision issue offhand.

Probably the best thing you can do is try to find a specific test case where data has this discrepancy, and see if you can reproduce it consistently. Then you can pick apart the logic a statement at a time and figure out where the discrepancy is introduced.

Is it possible you have a null callcharge or null taxamount? (null+(-$0.02)) => null; maybe

isnull(sum(callcharge+taxamount),0) 

should be:

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

@unbilledCallsAmount or @ArBalance might also be null if those select statements return no records.
HTH,

走过海棠暮 2024-08-12 15:33:57

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

What data types are callcharge and taxamount? I would do all my calculations in that data type and cast to decimal at the end. Currently all your intermediate vars are decimals which means that rounding errors compound. Don't round until the end.

憧憬巴黎街头的黎明 2024-08-12 15:33:57

当使用它时,并尝试不同的硬编码值作为值 callcharge &税额:

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 位或更多小数位,我只能得到不同的值。因此,如果您没有这样的数据,也没关系。

when using this, and trying different hard coded values for values 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 

I could only get the values to differ if you have 3 or more decimal places in callcharge or taxamount. So if you don't have data like that you are ok.

留一抹残留的笑 2024-08-12 15:33:57

由于 数据类型优先级。和金钱-> numeric (near end) 是四舍五入的,

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

Your money (from comment) columns are being converted to decimal(8,2) because of datatype precedence. And money-> numeric (near end) is rounded they are rounded

So both @unbilledCallsAmount and @ArBalance will be rounded and it's all cumulative.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文