【问题标题】:SQL Server - Strange Divide By ZeroSQL Server - 奇怪的被零除
【发布时间】:2008-12-17 17:42:00
【问题描述】:

我正在执行以下查询:

Select guiPolygonID, dbo.fn_Yval(p1X, p1Y, p2X, p2Y, 4.003318)
From [vPolygonSegments]
Where dbo.fn_Yval(p1X, p1Y, p2X, p2Y, 4.003318) > 0

函数fn_Yval的重要部分(所有参数都是float类型):

set @m = (@p2Y - @p1Y)/(@p2X - @p1X)
set @b = @p1Y - (@m*@p1X)
set @result = (@m*@xval+@b)

视图 vPolygonSegments 不包含 p1X = p2X 的记录(不包括这些记录)。然而,当我执行查询时,SQL Server 返回错误:“遇到除以零错误”。奇怪的是,如果我只执行前两行(没有 where 子句),查询返回结果就好了。

我该如何解决这个问题,和/或导致这种行为的原因是什么?

编辑: 这是我的看法:

Select P1.guiPolygonID,
    P1.decX as p1X, P1.decY as p1Y,
    P2.decX as p2X, P2.decY as p2Y
From PolygonPoints P1, PolygonPoints P2
Where P1.guiPolygonID = P2.guiPolygonID
    and (
        P1.lPointNumber - P2.lPointNumber = 1
        or (
            -- Some other unimportant code
        )
    )
    and P1.decX <> P2.decX

【问题讨论】:

  • 如果您使用此功能确定线段的角度,并且您的点在纬度/经度坐标系中,那么您会遇到更大的问题。问题是纬度变化一度与经度变化度不同。

标签: sql-server divide-by-zero


【解决方案1】:

这里的问题是 select Cause 中的函数在 where 子句中的函数被求值之前求值。这种情况非常罕见,但有可能,因此您需要针对它进行编码。我建议您修改该函数,以便它可以安全地处理除以零的情况。更改此行:

set @m = (@p2Y - @p1Y)/(@p2X - @p1X)

到这里:

set @m = (@p2Y - @p1Y)/NullIf((@p2X - @p1X), 0)

当@p2x - @p1x = 0 时,NULLIF 函数将返回 NULL。随后,@m 将为空,所有其他值也是如此。该函数很可能会返回 NULL。

在你的 where 子句中,你有...

Where dbo.fn_Yval(p1X, p1Y, p2X, p2Y, 4.003318) > 0

当函数返回 NULL 时,它不会与 0 进行比较,并且最终会被过滤掉。

【讨论】:

  • 同意你的看法。我从来没有写过任何使用除法的代码,如果没有确保它可以处理零值。
【解决方案2】:

正如 Joel Coehoorn 所指出的,这很可能是问题所在。下面的查询应该可以避免这个问题,但最好在你的函数内部解决这个问题:

Select guiPolygonID, dbo.fn_Yval(p1X, p1Y, p2X, p2Y, 4.003318)
From [vPolygonSegments]
Where p2X <> p1X and dbo.fn_Yval(p1X, p1Y, p2X, p2Y, 4.003318) > 0

【讨论】:

  • 我仍然不明白它为什么会中断(可能是预先计算或其他),但您的解决方案可以解决问题。
  • 我也不明白——这就是为什么我建议你对你的函数进行防弹。
【解决方案3】:

我怀疑这与如何为查询具体化视图有关。将该函数添加到 where 子句本质上会将其添加到视图的源查询中,这样优化器可能会选择在 P1.decX &lt;&gt; P2.decX 之前将此过滤器应用于源数据。

无论如何,您不知道何时要在没有相同约束的其他地方重用该功能。最好预先制定如何处理不良数据的计划。在这种情况下,NullIf 建议似乎是一个不错的建议。

【讨论】:

    【解决方案4】:

    当@p2X = @p1X 时出现问题。在这种情况下,您预计会发生什么?

    【讨论】:

    • .. 但他正在删除这些数据点(此外,在这种情况下,选择不会也失败 - 什么时候删除?)
    • 要么没有删除这些点,要么我们没有所有相关代码。
    • 他发布的代码在哪里删除了数据?当然不是 where 子句!
    • 我知道他是这么说的,但是这就是正在发生的事情……除非您可以在该查询中指出另一个除法运算,分母中可能为 0。
    • “奇怪的是,如果我只执行前两行(没有 where 子句),查询返回结果就好了。” - 这意味着不会发生这种情况?奇怪!
    【解决方案5】:

    我不知道这会以任何方式回答这个问题,但您正在为每条记录评估 fn_Yval 两次。为什么不将函数的结果作为视图中的列?然后,您的 where 子句可能类似于“where Yval > 0”。

    编辑:出于好奇,fn_Yval 没有任何副作用,是吗?

    【讨论】:

      【解决方案6】:

      当涉及连接或视图时,我发现尝试使用 where 子句删除可能导致除以零错误的数据是有问题的。要么过滤连接子句中的数据,要么在函数中过滤掉它。

      更新:出于“G Mastros”所写的那种原因。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-28
        • 2013-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多