如何避免SQL中的“除以零”错误?

时间:2009-05-14 06:06:05

标签: sql sql-server sql-server-2005 sql-server-2008

我有以下错误消息:

  

Msg 8134,Level 16,State 1,Line 1除以零错误。

编写SQL代码的最佳方法是什么,以便我再也不会看到此错误消息了?

我可以执行以下任一操作:

  • 添加一个where子句,使我的除数永远不为零

或者

  • 我可以添加一个case语句,以便对零进行特殊处理。

使用NULLIF子句的最佳方法是什么?

有更好的方法,或者如何执行?

19 个答案:

答案 0 :(得分:557)

为了避免“除零”错误,我们将其编程为:

Select Case when divisor=0 then null
Else dividend / divisor
End ,,,

但这是一个更好的方法:

Select dividend / NULLIF(divisor, 0) ...

现在唯一的问题是要记住NullIf位,如果我使用“/”键。

答案 1 :(得分:157)

如果您想要返回零,如果发生零分割,您可以使用:

SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable

对于零的每个除数,结果集中将得到零。

答案 2 :(得分:59)

这似乎是我尝试解决除零的最佳解决方案,这确实发生在我的数据中。

假设您想要计算各种学校俱乐部的男女比例,但是您发现以下查询失败并在尝试计算指环王俱乐部的比率时发出除零错误,没有女人:

SELECT club_id, males, females, males/females AS ratio
  FROM school_clubs;

您可以使用函数NULLIF来避免除以零。 NULLIF比较两个表达式,如果相等则返回null,否则返回第一个表达式。

将查询重写为:

SELECT club_id, males, females, males/NULLIF(females, 0) AS ratio
  FROM school_clubs;

任何数字除以NULL都会得到NULL,并且不会产生任何错误。

答案 3 :(得分:39)

您也可以在查询开头执行此操作:

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

因此,如果您有类似100/0的内容,则会返回NULL。我只是为简单的查询做了这个,所以我不知道它会如何影响更长/更复杂的查询。

答案 4 :(得分:31)

编辑: 我最近得到了很多关于这个... ...所以我想我只是添加一个注释,这个答案是在问题进行之前编写的,这是最近的编辑,其中返回null被突出显示为一个选项...似乎非常接受。我的一些答案是针对爱德华的关注,在评论中,他似乎主张回归0。这就是我反对的情况。

解答: 我认为这里存在一个潜在的问题,即除以0是不合法的。这表明某些事情是错误的。如果你除以零,你就会尝试做一些数学上没有意义的事情,所以你得到的数字答案都不会有效。 (在这种情况下使用null是合理的,因为它不是将在以后的数学计算中使用的值。)

所以Edwardo在评论中询问“如果用户输入0怎么办?”,他主张应该可以获得0作为回报。如果用户在金额中加零,并且你希望在他们这样做时返回0,那么你应该在业务规则级别放入代码来捕获该值并返回0 ...没有一些特殊情况,除以0 = 0.

这是一个微妙的区别,但它很重要......因为下次有人调用你的函数并期望它做正确的事情,它会做一些在数学上不正确的时髦,但只是处理特定的边缘情况它以后很有可能咬人。你并没有真正地除以0 ......你只是回答了一个糟糕的问题。

想象一下,我正在编写一些东西,我搞砸了。我应该在辐射测量比例值中读取,但在一个奇怪的边缘情况下我没有预料到,我在0中读到。然后我将我的价值放入你的功能......你给我一个0!华友世纪,没有辐射!除了它真的存在,只是我传递了一个不好的价值......但我不知道。我希望分区抛出错误,因为它是错误的标志。

答案 5 :(得分:31)

您至少可以阻止查询中断错误,如果除数为零,则返回NULL

SELECT a / NULLIF(b, 0) FROM t 

然而,我会从不将此转换为零coalesce,就像在另一个得到许多赞成票的答案中所示。这在数学意义上是完全错误的,甚至是危险的,因为您的应用程序可能会返回错误和误导性的结果。

答案 6 :(得分:21)

SELECT Dividend / ISNULL(NULLIF(Divisor,0), 1) AS Result from table

通过使用nullif()捕获零,然后使用isnull()得到的null可以绕过除以零的错误。

答案 7 :(得分:8)

更换"除以零"零是有争议的 - 但它也不是唯一的选择。在某些情况下,替换为1是(合理地)合适的。我经常发现自己在使用

 ISNULL(Numerator/NULLIF(Divisor,0),1)

当我查看分数/计数的变化时,如果我没有数据,则希望默认为1。例如

NewScore = OldScore *  ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1) 

通常,我实际上在其他地方计算了这个比率(尤其是因为它可以为低分母投入一些非常大的调整因子。在这种情况下,我通常对OldSampleScore的控制比大于一个阈值;然后排除零。但有时候' hack'是合适的。

答案 8 :(得分:5)

我在前一段时间写了一个函数来为我的stored procedures处理它:

print 'Creating safeDivide Stored Proc ...'
go

if exists (select * from dbo.sysobjects where  name = 'safeDivide') drop function safeDivide;
go

create function dbo.safeDivide( @Numerator decimal(38,19), @divisor decimal(39,19))
   returns decimal(38,19)
begin
 -- **************************************************************************
 --  Procedure: safeDivide()
 --     Author: Ron Savage, Central, ex: 1282
 --       Date: 06/22/2004
 --
 --  Description:
 --  This function divides the first argument by the second argument after
 --  checking for NULL or 0 divisors to avoid "divide by zero" errors.
 -- Change History:
 --
 -- Date        Init. Description
 -- 05/14/2009  RS    Updated to handle really freaking big numbers, just in
 --                   case. :-)
 -- 05/14/2009  RS    Updated to handle negative divisors.
 -- **************************************************************************
   declare @p_product    decimal(38,19);

   select @p_product = null;

   if ( @divisor is not null and @divisor <> 0 and @Numerator is not null )
      select @p_product = @Numerator / @divisor;

   return(@p_product)
end
go

答案 9 :(得分:3)

  1. 添加强制Divisor为非零的CHECK约束
  2. 向表单添加验证器,以便用户无法在此字段中输入零值。

答案 10 :(得分:3)

对于更新SQL:

update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)

答案 11 :(得分:2)

没有神奇的全局设置'转0除0例外'。操作必须抛出,因为x / 0的数学含义与NULL含义不同,所以它不能返回NULL。 我假设您正在处理显而易见的事情,并且您的查询具有应该消除具有0除数的记录并且从不评估除法的条件。通常的“问题”是大多数开发人员希望SQL表现得像过程语言并提供逻辑运算符短路,但它确实 NOT 。我建议你阅读这篇文章:http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html

答案 12 :(得分:2)

这是一种可以除以零的情况。业务规则是,为了计算库存周转率,您需要计算一段时间内销售的商品的成本,将其年化。获得年化数后,除以该期间的平均库存。

我正在考虑计算三个月内发生的库存周转次数。我已计算出我在三个月期间售出的商品成本为1,000美元。年销售额为4,000美元(1,000美元/ 3)* 12。初始库存为0.结束库存为0.我的平均库存现为0.我每年的销售额为4000美元,没有库存。这产生无限匝数。这意味着客户正在转换和购买我的所有库存。

这是如何计算库存周转率的业务规则。

答案 13 :(得分:1)

使用where子句过滤掉数据,这样就不会得到0值。

答案 14 :(得分:1)

CREATE FUNCTION dbo.Divide(@Numerator Real, @Denominator Real)
RETURNS Real AS
/*
Purpose:      Handle Division by Zero errors
Description:  User Defined Scalar Function
Parameter(s): @Numerator and @Denominator

Test it:

SELECT 'Numerator = 0' Division, dbo.fn_CORP_Divide(0,16) Results
UNION ALL
SELECT 'Denominator = 0', dbo.fn_CORP_Divide(16,0)
UNION ALL
SELECT 'Numerator is NULL', dbo.fn_CORP_Divide(NULL,16)
UNION ALL
SELECT 'Denominator is NULL', dbo.fn_CORP_Divide(16,NULL)
UNION ALL
SELECT 'Numerator & Denominator is NULL', dbo.fn_CORP_Divide(NULL,NULL)
UNION ALL
SELECT 'Numerator & Denominator = 0', dbo.fn_CORP_Divide(0,0)
UNION ALL
SELECT '16 / 4', dbo.fn_CORP_Divide(16,4)
UNION ALL
SELECT '16 / 3', dbo.fn_CORP_Divide(16,3)

*/
BEGIN
    RETURN
        CASE WHEN @Denominator = 0 THEN
            NULL
        ELSE
            @Numerator / @Denominator
        END
END
GO

答案 15 :(得分:0)

有时候,0可能不合适,但有时1也不合适。有时,从0到100,000,000的变化(描述为1或100%的变化)也可能会产生误导。在这种情况下,100,000,000%可能是合适的。这取决于您打算根据百分比或比率得出什么样的结论。

例如,一个很小的销售商品从2-4个出售,而一个很大的销售商品从1,000,000变为2,000,000,对于分析师或管理层来说可能意味着完全不同的东西,但两者都等于100 %或1个变化。

隔离NULL值可能比遍历一堆0%或100%包含合法数据的行要容易得多。通常,分母中的0表示错误或缺失值,并且您可能不想仅填充任意值只是为了使数据集看起来整洁。

CASE
     WHEN [Denominator] = 0
     THEN NULL --or any value or sub case
     ELSE [Numerator]/[Denominator]
END as DivisionProblem

答案 16 :(得分:0)

这是我固定的方法:

IIF(ValueA!= 0,总计/ ValueA,0)

它可以包装在更新中:

SET Pct = IIF(ValueA!= 0,总计/ ValueA,0)

或在选择项中:

SELECT IIF(ValueA!= 0,Total / ValueA,0)AS Pct FROM Tablename;

有想法吗?

答案 17 :(得分:-1)

使用NULLIF(exp,0)但以这种方式 - NULLIF(ISNULL(exp,0),0)

如果exp为NULLIF(exp,0)null不会中断,

NULLIF(ISNULL(exp,0),0)会中断

答案 18 :(得分:-1)

当它传播回调用程序时,您可以适当地处理错误(如果这是您想要的,则忽略它)。在C#中,SQL中发生的任何错误都会抛出一个异常,我可以捕获然后在我的代码中处理,就像任何其他错误一样。

我同意Beska的意见,因为你不想隐藏错误。您可能没有处理核反应堆,但一般隐藏错误是糟糕的编程实践。这是大多数现代编程语言实现结构化异常处理以将实际返回值与错误/状态代码分离的原因之一。当你做数学时尤其如此。最大的问题是你无法区分正确计算的0被返回或0作为错误的结果。相反,返回的任何值都是计算值,如果出现任何错误,则抛出异常。这当然会有所不同,具体取决于您访问数据库的方式以及您使用的语言,但您应始终能够收到可以处理的错误消息。

try
{
    Database.ComputePercentage();
}
catch (SqlException e)
{
    // now you can handle the exception or at least log that the exception was thrown if you choose not to handle it
    // Exception Details: System.Data.SqlClient.SqlException: Divide by zero error encountered.
}