2016-12-06 2 views
2

Скажем, у меня есть таблица линииКак внешнее предложение WHERE влияет на способ вложенного запроса?

b | a 
----------- 
17 7000 
17 0 
18 6000 
18 0 
19 5000 
19 2500 

Я хочу, чтобы получить положительные значения функции: (a1 - a2) \ (b2 - b1) для всех элементов в декартово произведение линий с различными Б. (Если вы заинтересованы это приведет к пересечениям линий y1 = b1*x + a1 и y2 = b2*x + a2)

Я написал QUERY1 для этой причины

SELECT temp.point FROM 
    (SELECT DISTINCT ((l1.a - l2.a)/(l2.b - l1.b)) AS point 
    FROM lines AS l1 
    CROSS JOIN lines AS l2 
    WHERE l1.b != l2.b 
    ) AS temp 
WHERE temp.point > 0 

Он бросает «деление на ноль» ошибка. Я попробовал один и тот же запрос без предложения WHERE (Query2), и она работает просто отлично

SELECT temp.point FROM 
    (SELECT DISTINCT ((l1.a - l2.a)/(l2.b - l1.b)) AS point 
    FROM lines AS l1 
    CROSS JOIN lines AS l2 
    WHERE l1.b != l2.b 
    ) AS temp 

, а также изменения с определенной функции SQL (query3)

CREATE FUNCTION get_point(@a1 DECIMAL(18, 4), @a2 DECIMAL(18, 4), @b1 INT, @b2 INT) 
RETURNS DECIMAL(18, 4) 
WITH EXECUTE AS CALLER 
AS 
BEGIN 
    RETURN (SELECT (@a1 - @a2)/(@b2 - @b1)) 
END 
GO 
SELECT temp.point FROM 
    (SELECT DISTINCT dbo.get_point(l1.a, l2.a, l1.b, l2.b) AS point 
    FROM lines AS l1 
    CROSS JOIN lines AS l2 
    WHERE l1.b != l2.b 
    ) AS temp 
WHERE temp.point > 0 

У меня есть интуитивное предположение о том, что внешний SELECT не должен влиять на способ вложенного SELECT выполняется (или, по крайней мере, не должен его ломать). Даже если это неверно, это не объясняет, почему query3 работает, когда query1 нет.

Может ли кто-нибудь объяснить принцип этого? Это было бы очень признательно.

ответ

2

Если вы хотите, чтобы гарантировать, что запрос всегда будет работать, вам нужно обернуть расчет в чем-то, как case заявление

case when l2.b - l1.b = 0 
    then null 
    else (l1.a - l2.a)/(l2.b - l1.b) 
end 

Технически, оптимизатор совершенно бесплатно оценить условия в любом порядке он ожидает, что будет более эффективным. Оптимизатор может свободно оценивать деление до предложения where, которое отфильтровывает строки, в которых делителем будет 0. Кроме того, он может сначала оценить предложение where. Ваши разные запросы имеют разные планы запросов, которые приводят к поведению.

Однако, хотя конкретный запрос может иметь «хороший» план запросов сегодня, нет никакой гарантии, что оптимизатор не решит в течение дня, месяца или года изменить план запроса на то, что бы выбросить деление на 0 ошибок. Я полагаю, вы могли бы решить использовать кучу советов/планов, чтобы заставить конкретный план использовать определенное поведение. Но это похоже на то, что укусит вас в задних помещениях позже. Объединение вычислений в case (или в противном случае предотвращение деления на 0 ошибок) будет гораздо более безопасным и легче объяснить следующему разработчику.

+0

Спасибо, теперь все имеет смысл – ablclanmarazm