外部 WHERE 子句如何影响嵌套查询的执行方式?

How does the outer WHERE clause affect the way nested query is executed?

假设我有 table

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

我想获得一个函数的正值:(a1 - a2) \ (b2 - b1) 对于具有不同 b 的 的笛卡尔积中的所有元素。 (如果您有兴趣,这将导致 y1 = b1*x + a1y2 = 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

它抛出 "division by zero" 错误。我在没有 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 无效。

谁能解释一下这背后的原理?那将不胜感激。

如果您想保证查询始终有效,您需要将计算包装在类似 case 语句

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

从技术上讲,优化器可以完全自由地按照它期望更有效的顺序评估条件。优化器可以自由地评估 where 子句之前的除法,该子句过滤掉除数为 0 的行。它也可以自由地首先评估 where 子句。您的不同查询具有不同的查询计划,这会导致不同的行为。

但实际上,即使特定查询今天可能有一个 "good" 查询计划,也不能保证优化器不会在一天、一个月或一年内决定改变计划查询计划会引发除以 0 的错误。我想你可以决定使用一堆提示/计划指南来强制使用具有特定行为的特定计划。但这往往是那种事后会在后肢咬你的东西。将计算包装在 case 中(或以其他方式防止除以 0 错误)将更安全,也更容易向下一个开发人员解释。