SQL Server - странное деление на ноль - PullRequest
0 голосов
/ 17 декабря 2008

Я выполняю следующий запрос:

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), запрос вернет результаты просто отлично.

Как мне это исправить и / или что вызывает такое поведение?

Edit: Вот мой взгляд:

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

Ответы [ 6 ]

3 голосов
/ 17 декабря 2008

Проблема здесь в том, что функция в причине выбора вычисляется перед функцией в предложении 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 голосов
/ 17 декабря 2008

Я подозреваю, что это связано с тем, как представление материализуется для запроса. Добавление этой функции к предложению where по существу добавляет ее к исходному запросу для представления, так что оптимизатор может выбрать применение этого фильтра к исходным данным до P1.decX <> P2.decX.

В любом случае, вы не знаете, когда захотите повторно использовать эту функцию где-то еще, у которой нет такого же ограничения. Лучше всего иметь план, как обрабатывать плохие данные заранее. NullIf предложения в этом случае кажутся хорошими.

2 голосов
/ 17 декабря 2008

Как отметил Джоэл Коухорн, это вероятная проблема. Приведенный ниже запрос должен избежать проблемы, но проблему лучше всего решить внутри самой функции:

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
1 голос
/ 17 декабря 2008

Проблема возникает, когда @ p2X = @ p1X. Что вы ожидаете, что произойдет в этом случае?

0 голосов
/ 17 декабря 2008

Мне показалось проблематичным попытаться удалить данные, которые могут привести к ошибкам деления на ноль, с помощью предложения where, когда используется соединение или представление. Либо отфильтруйте данные в предложении соединения, либо отфильтруйте их в функции.

обновление: только по причинам, написанным "G Mastros".

0 голосов
/ 17 декабря 2008

Я не знаю, что это каким-либо образом ответит на вопрос, но вы оцениваете fn_Yval дважды для каждой записи. Почему бы не сделать результат функции столбцом в представлении? Тогда ваше предложение where может быть чем-то вроде «where Yval> 0».

Редактировать: из любопытства, fn_Yval не имеет побочных эффектов, не так ли?

...