【问题标题】:SQL Server - divide by zero error in WHERE clause (even with condition divisor > 0)SQL Server - WHERE子句中除以零错误(即使条件除数> 0)
【发布时间】:2018-10-04 13:35:12
【问题描述】:

在使用涉及除法的WHERE 子句测试SQL 查询时,我遇到了一个看似奇怪的错误。任务是找出发票中任何以下情况为真的错误:

  • AmountTax 都为零(不是两者);
  • AmountTax 都不为零,但不满足一定的算术条件。

所以我想出的查询如下:

SELECT InvoiceNumber, InvoiceDate, Amount, Tax
FROM Invoices
    WHERE (Amount <> 0 AND Tax = 0)
    OR (Amount = 0 AND Tax <> 0)
    OR (Amount <> 0 AND Tax <> 0 AND ABS(Tax / Amount - 0.18) > 0.005)

为了让事情更奇怪,这个查询可以正常工作:

SELECT InvoiceNumber, InvoiceDate, Amount, Tax
FROM Invoices
    WHERE Amount <> 0 AND Tax <> 0 AND ABS(Tax / Amount - 0.18) > 0.005

我猜想 SQL Server 条件子句中的布尔求值不像在 C# 中那样工作(我可以期待惰性求值防止除以零错误)。

如何使这个查询正确?

注意:有数千万条记录,因此嵌套的SELECT 语句可能会影响性能。

【问题讨论】:

  • Amount &lt;&gt; 0.18 ?
  • 可能只是在表达式周围加上适当的括号,以确保您在判断运算符优先级时不会出错。
  • @Cid 不会先做除法吧,可能还需要ABS(Tax / (Amount - 0.18))
  • @SteveSmith 确实应该这样做,但谁知道优先级是否得到很好的尊重?
  • NULLIF(Amount, 0)。不允许除以 0,但除以 NULL 是允许的。正如您所发现的,强制优化器不计算表达式基本上是不可能的。即使CASE 也并不总是有效。

标签: sql sql-server divide-by-zero


【解决方案1】:

不要依赖于查询中子句或条件的评估顺序。时期。 优化器保留重新排列所有内容以提高性能的权利。 好吧,不是所有内容。 CASE 表达式有保证的求值顺序。

解决方案非常简单。使用NULLIF()

WHERE (Amount <> 0 AND Tax = 0) OR
      (Amount = 0 AND Tax <> 0) OR
      (Amount <> 0 AND Tax <> 0 AND ABS(Tax / NULLIF(Amount, 0) - 0.18) > 0.005)

WHERE Amount <> 0 AND Tax <> 0 AND ABS(Tax / NULLIF(Amount, 0) - 0.18) > 0.005

【讨论】:

  • 回复:CASE 表达式 - 除了聚合和其中的表达式,可能即使在不需要时也会被计算。
  • 我曾经和你一样是CASE的信徒,然后我把一个糟糕的执行计划带到了...缓存。关于 CASE 在某些特定情况下不进行快捷方式评估的错误报告一直被关闭(或忽略)为“按设计工作”/“无法修复”/“但通常这种方式更快”。
【解决方案2】:

你有没有尝试使用括号

 SELECT InvoiceNumber, InvoiceDate, Amount, Tax
FROM Invoices
    WHERE (Amount <> 0 AND Tax = 0)
    OR (Amount = 0 AND Tax <> 0)
    OR (Amount <> 0 AND Tax <> 0 AND ABS(Tax / Amount - 0.18) > 0.005)

在处理多个条件(或/与)时,尝试使用“()”进行分隔。

运算符的逻辑顺序是

  • 算术
  • 串联
  • 比较条件
  • IS [NOT] NULL、LIKE、[NOT] IN
  • [不] 之间
  • 不等于
  • 不是

您可以使用括号处理此规则。

【讨论】:

  • 方括号控制运算符的解释方式,但它们控制评估顺序(当然在 ANSI SQL 和 SQL Server 中。其他产品可能会提供一些保证)。
  • 对不起,绝对不是这样。我把所有多余的括号都放在了确保运算符优先级上,但仍然有 div/0 错误。
  • 是的,这是逻辑顺序。但是 SQL Server 非常乐意以不同的顺序评估谓词(只要它根据逻辑顺序正确组合结果),甚至会产生错误,因为它已经急切地评估了一个它不需要的谓词 if它确实遵循逻辑顺序并且实现了短路行为(它都不保证,也不要求符合SQL标准)
  • 尝试检查有关 SQL SERVER 括号和逻辑顺序的文档,我想你会发现它非常有用docs.microsoft.com/en-us/sql/t-sql/language-elements/…
  • 优化器不关心逻辑顺序。它会在它认为方便的任何时候愉快地评估子表达式,即使这些子表达式后来在逻辑上是多余的。这个逻辑顺序决定了表达式的解析语义不是子表达式被求值的顺序(或者它们是否被求值)。子表达式在所有可能的条件下都应该是格式正确的,否则你会被咬——逻辑顺序或否。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多