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

时间:2016-12-06 15:48:43

标签: sql-server where divide-by-zero cross-join

假设我有一张表

 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

它抛出“除以零”错误。我尝试了没有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 不起作用时也是如此。

有人可以解释这背后的原理吗?非常感谢。

1 个答案:

答案 0 :(得分: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错误)将更安全,更容易向下一位开发人员解释。