我有这个错误信息:

Msg 8134,级别16,状态1,第1行,除以零错误。

写SQL代码的最好方法是什么,这样我就再也不会看到这个错误消息了?

我可以做以下任何一件事:

添加where子句,使除数永远不为零

Or

我可以添加一个case语句,这样就可以对0进行特殊处理。

使用NULLIF子句是最好的方法吗?

有没有更好的办法,或者如何实施?


当前回答

编辑: 最近我得到了很多反对票…所以我想我只是添加了一个注释,这个答案是在问题经历了最近的编辑之前写的,返回null作为一个选项被高亮显示…这似乎是可以接受的。我的一些回答是针对Edwardo的担忧,在评论中,他似乎主张返回0。这就是我反对的案子。

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

所以Edwardo在评论中问道:“如果用户输入一个0呢?”他主张得到一个0应该是可以的。如果用户在金额中输入0,并且您希望在他们这样做时返回0,那么您应该在业务规则级别放入代码以捕获该值并返回0…没有一些特殊情况,除以0 = 0。

这是一个微妙的区别,但很重要……因为下次有人调用你的函数并希望它做正确的事情时,它做了一些奇怪的事情,从数学上来说是不正确的,但只是处理特定的边界情况,它有很好的机会在以后咬人。你不是真的除以0…你只是对一个糟糕的问题给出了一个糟糕的答案。

想象一下,我在写代码,结果搞砸了。我应该读取辐射测量缩放值,但在一个我没有预料到的奇怪边缘情况下,我读取的是0。然后我将我的值放入你的函数中…你给我一个0!万岁,没有辐射!除了它真的在那里,只是我传入了一个坏的值…但我不知道。我想让除法抛出错误因为它是出错的标志。

其他回答

你至少可以阻止查询被错误打断,如果有被0除,返回NULL:

SELECT a / NULLIF(b, 0) FROM t 

然而,我永远不会像其他得到很多赞的答案那样,用合并将其转换为零。从数学意义上讲,这是完全错误的,甚至是危险的,因为您的应用程序可能会返回错误和误导性的结果。

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

I agree with Beska in that you do not want to hide the error. You may not be dealing with a nuclear reactor but hiding errors in general is bad programming practice. This is one of the reasons most modern programming languages implement structured exception handling to decouple the actual return value with an error / status code. This is especially true when you are doing math. The biggest problem is that you cannot distinguish between a correctly computed 0 being returned or a 0 as the result of an error. Instead any value returned is the computed value and if anything goes wrong an exception is thrown. This will of course differ depending on how you are accessing the database and what language you are using but you should always be able to get an error message that you can deal with.

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.
}
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

添加一个CHECK约束,强制除数为非零 向表单添加验证器,以便用户不能在该字段中输入零值。

编辑: 最近我得到了很多反对票…所以我想我只是添加了一个注释,这个答案是在问题经历了最近的编辑之前写的,返回null作为一个选项被高亮显示…这似乎是可以接受的。我的一些回答是针对Edwardo的担忧,在评论中,他似乎主张返回0。这就是我反对的案子。

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

所以Edwardo在评论中问道:“如果用户输入一个0呢?”他主张得到一个0应该是可以的。如果用户在金额中输入0,并且您希望在他们这样做时返回0,那么您应该在业务规则级别放入代码以捕获该值并返回0…没有一些特殊情况,除以0 = 0。

这是一个微妙的区别,但很重要……因为下次有人调用你的函数并希望它做正确的事情时,它做了一些奇怪的事情,从数学上来说是不正确的,但只是处理特定的边界情况,它有很好的机会在以后咬人。你不是真的除以0…你只是对一个糟糕的问题给出了一个糟糕的答案。

想象一下,我在写代码,结果搞砸了。我应该读取辐射测量缩放值,但在一个我没有预料到的奇怪边缘情况下,我读取的是0。然后我将我的值放入你的函数中…你给我一个0!万岁,没有辐射!除了它真的在那里,只是我传入了一个坏的值…但我不知道。我想让除法抛出错误因为它是出错的标志。