Question

I'm executing the following query:

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

The important parts of function fn_Yval (all params are of type float):

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

The view vPolygonSegments contains no records where p1X = p2X (those records are excluded). Yet, when I execute my query, SQL Server returns an error: "Divide by zero error encountered." Curiously, if I execute only the first two lines (without the where clause), the query returns results just fine.

How do I fix this, and/or what is causing this behavior?

Edit: Here is my view:

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
Was it helpful?

Solution

The problem here is that the function in the select cause is evaluated before the function in the where clause is evaluated. This condition is very rare, but it is possible, so you need to code against it. I would suggest that you modify the function so that it can safely handle divide by zero circumstances. Change this line:

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

To this:

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

When @p2x - @p1x = 0, the NULLIF function will return NULL. Subsequently, @m will be null, as will all the other values. Most likely, the function will return NULL.

In your where clause, you have...

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

When the function returns NULL, it won't compare to 0 and will end up getting filtered out anyway.

OTHER TIPS

As Joel Coehoorn pointed out, that is the likely issue. The query below should avoid the problem, but the problem is best fixed inside your function itself:

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

I suspect it has to do with how the view is materialized for the query. Adding that function to the where clause essentially adds it to the source query for the view, such that the optimizer might choose to apply this filter to the source data before P1.decX <> P2.decX.

Anyway, you don't know when you'll want to re-use that function somewhere else that doesn't have the same constraint. Best to have a plan for how to handle bad data up front. The NullIf suggestions seems like a good one in this case.

The problem occurs when @p2X = @p1X. What do you expect to happen in that case?

I don't know that this will answer the question in any way, but you're evaluating fn_Yval twice for each record. Why not make the result of the function a column in the view? Then, your where clause could be something like "where Yval > 0".

Edit: Out of curiosity, fn_Yval doesn't have any side effects, does it?

I found it to problematic to try to remove data that can cause divide by zero errors using a where clause when a join or view is involved. Either filter the data in the join clause or filter it out in the function.

update: for just the kind of reasons "G Mastros" wrote.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top