假设我有一张表行
b | a
-----------
17 7000
17 0
18 6000
18 0
19 5000
19 2500
我希望得到一个函数的正值:(a1 - a2) \ (b2 - b1)
用于行的笛卡尔积中的所有元素和不同的b。 (如果您有兴趣,这会导致行 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 不起作用时也是如此。
有人可以解释这背后的原理吗?非常感谢。
答案 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错误)将更安全,更容易向下一位开发人员解释。