【问题标题】:Extra parenthesis changing result of formula in SQL Server 2008SQL Server 2008中公式的额外括号更改结果
【发布时间】:2012-09-09 07:09:05
【问题描述】:

在所有其他语言(通常是算术引擎)中,在相同优先级的运算符周围放置一组额外的括号不会影响结果。但最近在一个测试项目中,我注意到 MS SQL 服务器在这些情况下会改变结果。请查看下面的查询,如果您有任何想法(或 SQL Server 管理中的设置)或任何指向 MSDN 文章的链接来解释该行为,请告诉我。

select (0.55 * 287.61 / 0.66) calc_no_parens
,(0.55 * (287.61 / 0.66)) calc_parens
,round(0.55 * 287.61 / 0.66,2) no_paren_round
,round(0.55 * (287.61 / 0.66),2) paren_round;

结果

Column  Record 1
calc_no_parens  239.6750000
calc_parens     239.67499985
no_paren_round  239.6800000
paren_round     239.67000000

对我来说,前两个应该返回 239.675,回合应该返回 239.68。

【问题讨论】:

  • 答案有何不同?这可能只是一个浮点舍入/精度问题吗?
  • 它们相差一小部分(请参阅我的问题中的更新)。但这在任何其他编程平台中都不是问题。但存在于 IBM DB2 和 MS SQL Server 中。我没有方便试用的 Oracle 服务器

标签: sql sql-server arithmetic-expressions


【解决方案1】:

如果您将每个值声明为Float,您将获得所需的结果。

DECLARE @Float1 float, @Float2 float, @Float3 float;
SET @Float1 = 0.55;
SET @Float2 = 287.61;
SET @Float3 = 0.66;

select (@Float1 * @Float2 / @Float3) calc_no_parens
,(@Float1* (@Float2/ @Float3)) calc_parens
,round(@Float1 * @Float2/ @Float3,2) no_paren_round
,round(@Float1* (@Float2/ @Float3),2) paren_round;

输出

calc_no_parens  calc_parens no_paren_round  paren_round
239.675          239.675    239.68           239.68

你可能想看这篇文章:So-called "exact" numerics are not at all exact!

【讨论】:

  • 实际上我不能这样做,因为我选择了几个小数列并在查询中进行数学运算,并将其与 Informatica ETL 作业结果进行比较。但是您对 Float 的看法是正确的,这让我在 MSDN 中找到了一篇关于小数精度的文章。请看下面我的回答
【解决方案2】:

我可以看到正在发生的事情,但我认为没有解决办法。

SQL 将函数的每个部分计算并存储为 SQL 数据类型(在本例中为浮点数)。

287.61/0.66 产生 435.7727272727272727272727272... SQL 将存储为浮点数以达到一定的准确性,但它并不准确(毕竟,它是一个浮点数)。

有关浮点数的更多信息:How is floating point stored? When does it matter?

【讨论】:

    【解决方案3】:

    Habib 的回答让我认为这必须是我的列使用的十进制数据类型。经过一番研究,我发现了这个 Precision, Scale, and Length (Transact-SQL)

    正如您在那篇文章中看到的那样,除法运算显着改变了所得小数的比例和精度。然后我尝试了我的查询的变体,这次在乘法运算周围添加了额外的括号。

    select distinct (0.55 * 287.61 / 0.66) calc_no_parens
    ,(0.55 * (287.61 / 0.66)) calc_parens_div
    ,((0.55 * 287.61) / 0.66) calc_parens_mult
    ,round(0.55 * 287.61 / 0.66,2) no_paren_round
    ,round(0.55 * (287.61 / 0.66),2) paren_round
    ,round((0.55 * 287.61) / 0.66,2) paren_round2;
    

    结果

    Column              Record 1
    calc_no_parens      239.6750000
    calc_parens_div     239.67499985
    calc_parens_mult    239.6750000
    no_paren_round      239.6800000
    paren_round         239.67000000
    paren_round2        239.6800000
    

    只要除法是公式中的最后一个运算符,我们就会得到正确的答案。这不是解决问题,而是在未来的任何测试项目中学习自我。

    【讨论】:

    • 乘法周围的括号将使 SQL 执行与没有任何括号完全相同的事情,因为 SQL 将从左到右进行乘法和除法运算。如果我没记错我的高中数学,你的 calc_no_parens 和 calc_parens_mult 是完全相同的方程
    • 我知道 Greg,我认为我的观点是强调相同优先级操作的顺序在十进制数学方面确实很重要。我的高中数学一定教给你的是 a * b / c = a / c * b。应该是因为 * 和 / 运算符具有相同的优先级,并且数学不应该根据操作顺序而改变。但实际上,确实如此。如果你先除法再乘法,你会失去精确度,你的答案也会变得不那么精确。
    • 抱歉,我无意冒犯。完全同意你的看法
    【解决方案4】:

    当您使用数字时,SQL 尝试动态转换它们:

    {

    SELECT 
    0.55*(287.61 / 0.66) PrecisionError, 
    0.55* (CONVERT(NUMERIC(24,12), 287.61) / CONVERT(NUMERIC(24,12), 0.66)) NotPrecisionError
    
    DECLARE @V SQL_VARIANT
    SET @V = 0.55*(287.61 / 0.66)
    
    SELECT 
    Value = @V
    ,[TYPE] = CONVERT(SYSNAME, sql_variant_property(@V, 'BaseType')) + '(' + 
              CONVERT(VARCHAR(10), sql_variant_property(@V, 'Precision')) + ',' + 
              CONVERT(VARCHAR(10), sql_variant_property(@V, 'Scale')) + ')'  
    
    SET @V = 0.55 * (CONVERT(NUMERIC(24,14), 287.61) / CONVERT(NUMERIC(24,14), 0.66))
    SELECT 
    Value = @V
    ,[TYPE] = CONVERT(SYSNAME, sql_variant_property(@V, 'BaseType')) + '(' + 
              CONVERT(VARCHAR(10), sql_variant_property(@V, 'Precision')) + ',' + 
              CONVERT(VARCHAR(10), sql_variant_property(@V, 'Scale')) + ')'
    

    }

    结果

    PrecisionError NotPrecisionError

    239.67499985 239.6750000000000

    类型

    239.67499985 数字(14,8)

    类型

    239.6750000000000 数字(38,13)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多