为什么SQL Server在运行存储过程时抛出“除以0”,而不会在独立语句中发生

为什么SQL Server在运行存储过程时抛出“除以0”,而不会在独立语句中发生,sql,sql-server,stored-procedures,Sql,Sql Server,Stored Procedures,此代码仅在运行存储过程时抛出除法0错误,不会在独立语句中发生 我想知道作为存储过程执行和作为公共语句执行之间的区别导致了这种情况 表PPB_PBExecuteLine有大约200条记录,有67条记录,数量为0,但在指定的情况下,仅执行select不会获得数量=0的记录 声明如下: --exec PPB_P_ReCalcActaulExeSUMToPB 1001603230260241,-1,1001605170110011 Create Procedure PPB_P_ReCalcActaul

此代码仅在运行存储过程时抛出除法0错误,不会在独立语句中发生

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

表PPB_PBExecuteLine有大约200条记录,有67条记录,数量为0,但在指定的情况下,仅执行select不会获得数量=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

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

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

这很难解释。但是,首先,使用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不能保证表达式的处理顺序。事实上,它可以在何处进行过滤之前推送一些计算

瞧!一个错误


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

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

在存储过程中

SUM(C.ExecuteCost - (B.OutExeCome / B.Qty) * C.Qty) as DiffCost
如果B.Qty=0,将抛出一个错误,因此您需要使用以下方法之一处理该问题:

用例:

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

(CASE WHEN B.Qty <> 0 THEN B.Qty ELSE 1 )
ARITHABORT和ANSI_警告

您可以将arithaport和ANSI_警告设置为OFF,这样,SQL server将返回带零除法的NULL

SET ARITHABORT OFF
SET ANSI_WARNINGS OFF

是的,它会解决这个问题。但是,我只是不明白为什么相同的陈述会有不同的结果。谢谢你的回复。这似乎是由订单更改引起的。这不是一个严重的错误,我应该记住它。@Kegegege-Erland Sommarskog IIRC将这些描述为,自从它被移动到azure.com之后,所有的作者信息似乎都丢失了。SQL引擎应该遵循逻辑处理顺序。它们可以自由地重新安排实际的处理顺序,只要它们产生相同的结果。然而,MS已将第一部分重新安排处理,并产生与结果集不同的结果错误。没有迹象表明他们会改变。不合逻辑的错误,一个非常恰当的表达。MS可能需要更严格,也会带来更多的工作。我想我已经知道了原因,非常感谢。
SET ARITHABORT OFF
SET ANSI_WARNINGS OFF