views:

908

answers:

6

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
+1  A: 

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

Joel Coehoorn
.. but he's removing such data points (besides wouldn't the select also fail in that case - when where is remove ?)
Learning
Either such points aren't removed or we don't have all of the relevant code.
marcumka
where in the code he posted is that data removed? certainly not the where clause!
Joel Coehoorn
"The view vPolygonSegments contains no records where p1X = p2X"...
GalacticCowboy
I know he said that, but nevertheless this is what's happening... unless you can point out another division operation in that query with the potential for a 0 in the denominator.
Joel Coehoorn
"Curiously, if I execute only the first two lines (without the where clause), the query returns results just fine." - that would imply that such condition does not occur? Strange!
Learning
Hmmm... somehow missed it was a view. That explains part of it.
Joel Coehoorn
I missed that it was a view too - a guess is that the query execution plan has decided to evaluate the outer where clause before the inner one in the loop. I don't even know if this is possible but it is the only thing I can come up with. Soldarnal, can you post the execution plan?
RedFilter
oops, loop = view, above
RedFilter
+2  A: 

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
RedFilter
I still don't understand why it breaks (maybe pre-calculating or something), but your solution fixes the problem.
Soldarnal
I don't understand that either - that is why I suggest you bulletproof your function instead.
RedFilter
A: 

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?

GalacticCowboy
A: 

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.

Booji Boy
+3  A: 

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.

G Mastros
Agree with you.I never ever write any code that uses division without making sure it can handle if a zero value happens.
HLGEM
+2  A: 

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.

Joel Coehoorn