Есть временная таблица следующего вида:
CREATE TABLE #1 (
num1 FLOAT
,num2 FLOAT
)
INSERT INTO #1
SELECT 0
,0
INSERT INTO #1
SELECT 0
,1
INSERT INTO #1
SELECT 1
,0
INSERT INTO #1
SELECT 1
,1
Выполняю следующий запрос и он валится с ошибкой:
SELECT *
FROM #1 a
WHERE (
a.num1 > 0
AND a.num2 = 0
)
OR (
a.num1 > 0
AND a.num2 > 0
AND a.num1 / a.num2 >= 2
)
OR (
a.num1 >= 0
AND a.num2 < 0
)
Divide by zero error encountered.
Покурил справку, там написано, что операция деления выполняется раньше всех логических операций. Ок, теперь стало понятно, почему валится с ошибкой.
Но потом я убираю знак "=" из последнего условия и все работает:
SELECT *
FROM #1 a
WHERE (
a.num1 > 0
AND a.num2 = 0
)
OR (
a.num1 > 0
AND a.num2 > 0
AND a.num1 / a.num2 >= 2
)
OR (
a.num1 > 0
AND a.num2 < 0
)
Посмотрел план выполнения запроса:
Судя по плану выполнения запроса, должна была снова возникнуть ошибка деления на ноль. Но этого не произошло. Проверял на Microsoft SQL Server-е версии 2005 и 2012. Проблема такая везде присутствует. В Teradata первый запрос отрабатывает без проблем.
Мой вопрос не в том, как переписать запрос, чтобы все заработало, а ПОЧЕМУ оно так работает. Почему, когда во втором запросе я убираю знак "=" запрос отрабатывает без проблем?
Ответ
По-видимому дело вот в чём.
Посмотрите план выполнения (XML). Когда вы убираете =, то предикат оптимизируется в (отредактировано для краткости)
т.е.
a.num1 > 0
AND
(
a.num2 = 0
OR
a.num1 / a.num2 >= 2 AND a.num2 > 0
OR
a.num2 < 0
)
Строки данных (0, 0) и (0, 1) отсекаются по условию a.num1 > 0. Строки (1, 0) и (1, 1) ему удовлетворяет, но (1, 0) проходит по условию a.num2 = 0 (до деления не доходит). Строка (1, 1) не проходит ни по одному условию OR.
Для сравнения, в неизменённом запросе предикат (также отредактировано для краткости), взятый из оценочного плана запроса, выглядит так:
Комментариев нет:
Отправить комментарий