为什么SQL Server抛出"除以0"在运行存储过程中,不会发生在独立声明中

时间:2018-02-08 03:33:04

标签: sql sql-server stored-procedures

此代码将"除以0和#34;仅运行存储过程时出错,并且不会在独立语句中发生。

我想知道执行存储过程和公共语句之间的区别是什么导致了这种情况。

PPB_PBExecuteLine有大约200条记录,有67条记录,数量为0,但只有select才能在指定情况下得到Qty = 0记录

以下是声明:

--exec PPB_P_ReCalcActaulExeSUMToPB 1001603230260241,-1,1001605170110011

Create Procedure PPB_P_ReCalcActaulExeSUMToPB
    @Org Bigint,
    @CBS Bigint,
    @ProjectIDs Nvarchar(max) 
with recompile
as
Begin
    Declare @SQL Nvarchar(Max)

    If Object_ID('tempdb..#Projects') is not null
            drop table #Projects;

    Create table #Projects (ID bigint null);

    If(@ProjectIDs != '')
    Begin
        Set @SQL = 'insert into #Projects (ID)' + 'select ' + replace(@ProjectIDs,',',' as ID union all select ') 
        Exec sp_executesql @SQL
    End

    If object_id('tempdb..#ProjectBudget_Detail_DiffCost') Is Not Null
        Drop Table #ProjectBudget_Detail_DiffCost;

    --*******this statement
    Select 
        B.ID as ExecuteLine,
        SUM(C.ExecuteCost - (B.OutExeCome / B.Qty) * C.Qty) as DiffCost
    Into 
        #ProjectBudget_Detail_DiffCost
    From 
        PPB_PBExecute A
    Inner Join 
        PPB_PBExecuteLine B on A.ID = B.PBExecute
    Inner Join 
        PPB_PBExecuteDetial C on C.PBExecuteLine = B.ID 
    Where 
        A.Org = @Org 
        and (A.CBS = @CBS or @CBS <= 0) 
        and (A.Project in (select ID from #Projects) or @ProjectIDs = '')
    Group by 
        B.ID

    Update A 
    Set A.DiffCost = B.DiffCost 
    From PPB_PBExecuteLine as A 
    Inner Join #ProjectBudget_Detail_DiffCost as B on A.ID = B.ExecuteLine
   --and more 
End

3 个答案:

答案 0 :(得分:1)

从我在你的存储过程中看到的,只有一行导致异常,这是在

SUM(C.ExecuteCost - (B.OutExeCome / B.Qty) * C.Qty ) as DiffCost

B.Qty为0时,它将抛出异常。但是,可以通过在CASE表达式的列中添加额外验证来防止这种情况。

SUM(C.ExecuteCost - (B.OutExeCome / (CASE WHEN B.Qty = 0 THEN 1 ELSE B.Qty END) * C.Qty) as DiffCost

答案 1 :(得分:0)

解释起来相当复杂。首先,使用nullif()来消除问题的可能性:

Select B.ID as ExecuteLine,
       SUM( C.ExecuteCost-(B.OutExeCome/nullif(B.Qty, 0))*C.Qty ) as DiffCost

(或者您可能需要其他一些逻辑。)

一种可能性是,B.Qty实际上是导致问题的0。让我推测这不是问题所在,因为你在一个地方看到了错误而在另一个地方看不到。

这导致0可能会被过滤掉。如何在过滤掉的行上出错?好吧,SQL Server不保证表达式的处理顺序。实际上,它可以在where

中进行过滤之前推送一些计算

瞧!错误。

为什么会发生在一个地方而不是另一个地方呢?这可能是由于不同的执行计划。也许存储过程中的版本已缓存,最佳计划已更改。

答案 2 :(得分:0)

数学除以零是未定义的。因此,在SQL Server中,也将是未定义的,这意味着它不是整数也不是NULL。

在您的存储过程中,

SUM(C.ExecuteCost - (B.OutExeCome / B.Qty) * C.Qty) as DiffCost

如果B.Qty = 0,则会抛出错误,因此您需要使用以下方法之一来处理:

使用案例

您可以使用CASE senario并将值Zero替换为数字或NULL。在这里,我将用数字1替换它。

(CASE WHEN B.Qty <> 0 THEN B.Qty ELSE 1 )

使用NULLIF

这是最快的方法,如果值等于零,将返回NULL。

NULLIF(B.Qty, 0)

ARITHABORT和ANSI_WARNINGS

您可以将ARITHABORT和ANSI_WARNINGS设置为OFF,这样,SQL服务器将返回带零分区的NULL。

SET ARITHABORT OFF
SET ANSI_WARNINGS OFF