我有此错误信息:
消息8134,级别16,状态1,第1行除以零错误。
编写SQL代码以使我再也看不到此错误消息的最佳方法是什么?
我可以执行以下任一操作:
- 添加一个where子句,这样我的除数永远不会为零
要么
- 我可以添加一个case语句,以便对零进行特殊处理。
使用NULLIF
子句的最佳方法是吗?
有没有更好的方法,或者如何执行?
我有此错误信息:
消息8134,级别16,状态1,第1行除以零错误。
编写SQL代码以使我再也看不到此错误消息的最佳方法是什么?
我可以执行以下任一操作:
要么
使用NULLIF
子句的最佳方法是吗?
有没有更好的方法,或者如何执行?
Answers:
为了避免出现“被零除”错误,我们对此进行了如下编程:
Select Case when divisor=0 then null
Else dividend / divisor
End ,,,
但这是一种更好的方法:
Select dividend / NULLIF(divisor, 0) ...
现在唯一的问题是,如果我使用“ /”键,则要记住NullIf位。
IsNull
而不是NullIf
吗?自己尝试!SELECT Value,1/NullIf(Value,0)FROM(VALUES(0),(5.0),(NULL))x(Value);
除非通过“ breaks”,否则您的意思是返回NULL?您可以使用IsNull
或将其转换为所需的内容Coalesce
。
SELECT 1 / NULLIF(NULL, 0)
失败了,但这是因为NULLIF()
需要知道第一个参数的数据类型。这个修改后的示例可以正常工作:SELECT 1 / NULLIF(CAST(NULL AS INT), 0)
。在现实生活中,您将提供一个表列NULLIF()
而不是一个NULL
常量。由于表列具有已知的数据类型,因此也可以正常工作:SELECT 1 / NULLIF(SomeNullableColumn, 0) FROM SomeTable
。
如果您想返回零,或者发生零除法,则可以使用:
SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable
对于除数为零的每个除数,您将在结果集中得到零。
假设您要计算各种学校俱乐部的男女比例,但是您发现以下查询失败,并且在尝试为没有女性的指环王俱乐部计算比例时,出现零除错误。 :
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
Give NULL
,都不会产生错误。
select males/(males+females), females/(males+females)
。这将为您提供俱乐部中男性和女性的百分比分布,例如31%的男性,69%的女性。
您也可以在查询开始时执行此操作:
SET ARITHABORT OFF
SET ANSI_WARNINGS OFF
因此,如果您有类似的东西100/0
,它将返回NULL。我仅对简单查询执行了此操作,因此我不知道它将如何影响较长/复杂的查询。
编辑:最近我对此表示了很多反对...所以我想我只想补充一下,这个答案是在问题进行最新编辑之前写的,其中高亮显示返回null是一个选项。 。这似乎是可以接受的。我的一些回答是针对Edwardo之类的担忧,他们在评论中似乎主张返回0。这就是我所反对的情况。
答案:我认为这里有一个潜在的问题,那就是除以0是不合法的。这表明根本上有问题。如果您将其除以零,则您正在尝试做一些数学上没有意义的事情,因此您无法获得任何数字答案都是有效的。(在这种情况下使用null是合理的,因为在以后的数学计算中不会使用该值)。
因此Edwardo在评论中问“用户是否输入0?”,他主张可以得到0作为回报。如果用户将金额设置为零,并且您希望在这样做时返回0,那么您应该在业务规则级别输入代码以捕获该值并返回0 ...不存在除以0 =的特殊情况0。
这是一个细微的差异,但是很重要...因为下次有人调用您的函数并期望它执行正确的操作时,它执行的某些时髦操作在数学上是不正确的,但仅能处理特定的边沿情况以后再咬人的好机会。您并没有真正除以0 ...您只是在向一个错误的问题返回错误的答案。
想象一下我正在编码,然后搞砸了。我应该读取辐射测量的比例值,但是在一个意料不到的边缘情况下,我读取了0。然后将我的值放到您的函数中……给我返回0!万岁,没有辐射!除非它真的在那里而且只是我传递了一个错误的值...但是我不知道。我希望除法器抛出错误,因为这是某些问题的标记。
用零代替“被零除”是有争议的-但这也不是唯一的选择。在某些情况下,(合理地)替换为1是适当的。我经常发现自己在用
ISNULL(Numerator/NULLIF(Divisor,0),1)
当我查看分数/计数的变化时,如果我没有数据,则想默认为1。例如
NewScore = OldScore * ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1)
通常,我实际上已经在其他地方计算了该比率(尤其是因为它可以为低分母抛出一些非常大的调整因子。在这种情况下,我通常会控制OldSampleScore大于阈值;然后排除零)但是有时候“ hack”是适当的。
我不久前写了一个函数来处理我的存储过程:
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
对于更新SQL:
update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)
没有魔术全局设置“关闭除以0的异常”。由于x / 0的数学含义不同于NULL的含义,因此必须抛出该操作,因此它不能返回NULL。我假设您正在处理显而易见的问题,并且您的查询具有应消除除数为0的记录且从不评估除法的条件。通常,“陷阱”是大多数开发人员所期望的,SQL的行为类似于过程语言并提供逻辑运算符短路,但事实并非如此。我建议您阅读这篇文章:http : //www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html
在这种情况下,您可以除以零。业务规则是,要计算库存周转率,您需要计算一段时间的销售成本并将其年化。获得年度数字后,用该时期的平均库存除以。
我正在计算三个月内发生的库存周转次数。我计算出在三个月的时间里售出的商品成本为$ 1,000。年销售率为$ 4,000($ 1,000 / 3)* 12。期初库存为0。期末库存为0。我现在的平均库存为0。我每年的销售额为$ 4000,并且没有库存。这产生了无限的匝数。这意味着我所有的库存都在被客户转换和购买。
这是如何计算库存周转率的业务规则。
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
有时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
这是我解决的方法:
IIF(ValueA!= 0,总计/ ValueA,0)
它可以包装在一个更新中:
SET Pct = IIF(ValueA!= 0,总计/ ValueA,0)
或选择:
从表名中选择SELECT IIF(ValueA!= 0,Total / ValueA,0)AS Pct;
有什么想法吗?
当错误传播回调用程序时,您可以适当地处理该错误(如果需要,可以忽略该错误)。在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.
}
使用NULLIF(exp,0)
,但以这种方式-NULLIF(ISNULL(exp,0),0)
NULLIF(exp,0)
如果exp是null
但NULLIF(ISNULL(exp,0),0)
不会破坏则中断