从this question、a neat answer about using COALESCE到简化复杂的逻辑树。我考虑了短路的问题。
例如,在大多数语言的函数中,参数都是经过充分计算的,然后传递给函数。在C中:
int f(float x, float y) {
return x;
}
f(a, a / b) ; // This will result in an error if b == 0这似乎不是SQL Server中COALESCE“函数”的限制:
CREATE TABLE Fractions (
Numerator float
,Denominator float
)
INSERT INTO Fractions VALUES (1, 1)
INSERT INTO Fractions VALUES (1, 2)
INSERT INTO Fractions VALUES (1, 3)
INSERT INTO Fractions VALUES (1, 0)
INSERT INTO Fractions VALUES (2, 0)
INSERT INTO Fractions VALUES (3, 0)
SELECT Numerator
,Denominator
,COALESCE(
CASE WHEN Denominator = 0 THEN 0 ELSE NULL END,
CASE WHEN Numerator <> 0 THEN Numerator / Denominator ELSE NULL END,
0
) AS TestCalc
FROM Fractions
DROP TABLE Fractions如果它评估的是第二种情况,当Denominator = 0时,我预计会看到如下错误:
Msg 8134, Level 16, State 1, Line 1
Divide by zero error encountered.我找到了一些甲骨文的mentions related。以及SQL Server的一些测试。当您包含用户定义的函数时,短路可能会中断。
那么,这种行为应该得到ANSI标准的保证吗?
发布于 2009-02-03 04:40:22
我刚刚看了一下链接的文章,可以确认COALESCE和ISNULL的短路都会失败。
如果涉及任何子查询,它似乎都会失败,但它对于标量函数和硬编码值很有效。
例如,
DECLARE @test INT
SET @test = 1
PRINT 'test2'
SET @test = COALESCE(@test, (SELECT COUNT(*) FROM sysobjects))
SELECT 'test2', @test
-- OUCH, a scan through sysobjectsCOALESCE是根据ANSI standard实现的。它只是CASE语句的简写。ISNULL不是ANSI标准的一部分。第6.9节似乎没有显式地要求短路,但它确实意味着应该返回when语句中的第一个true子句。
下面是一些适用于基于标量的函数的证明(我是在SQL Server 2005上运行的):
CREATE FUNCTION dbo.evil
(
)
RETURNS int
AS
BEGIN
-- Create an huge delay
declare @c int
select @c = count(*) from sysobjects a
join sysobjects b on 1=1
join sysobjects c on 1=1
join sysobjects d on 1=1
join sysobjects e on 1=1
join sysobjects f on 1=1
return @c / 0
END
go
select dbo.evil()
-- takes forever
select ISNULL(1, dbo.evil())
-- very fast
select COALESCE(1, dbo.evil())
-- very fast下面是CASE的底层实现将执行子查询的一些证据。
DECLARE @test INT
SET @test = 1
select
case
when @test is not null then @test
when @test = 2 then (SELECT COUNT(*) FROM sysobjects)
when 1=0 then (SELECT COUNT(*) FROM sysobjects)
else (SELECT COUNT(*) FROM sysobjects)
end
-- OUCH, two table scans. If 1=0, it does not result in a table scan.发布于 2009-02-03 05:08:24
在MS SQL Server中保证短路的有效方法是用例。对于成功的WHEN子句,不计算其他子句。
COALESCE can have issues
在本例中,为什么在COALESCE/CASE构造中有这么多分支?
SELECT Numerator
,Denominator
,CASE
WHEN Denominator = 0 THEN 0 END,
ELSE Numerator / Denominator
END AS TestCalc
FROM Fractions发布于 2009-02-03 04:21:27
我也很惊讶地看到答案是有效的!我不确定这种行为是不是有保证。(但我找不到一个不起作用的例子!)
在SQL工作了五年,我仍然很惊讶。
我还继续做了一个更改:
INSERT INTO #Fractions VALUES (0, 0)
SELECT Numerator
,Denominator
,coalesce (
CASE WHEN Denominator = 0 THEN 0 ELSE NULL END,
CASE WHEN Numerator <> 0 THEN Numerator / Denominator ELSE NULL END)
AS TestCalc
FROM #Fractions我得到的结果是:
Numerator Denominator TestCalc
1 1 1
1 2 0.5
1 3 0.3333333333333335
1 0 0
2 0 0
3 0 0
0 0 0现在我更困惑了!对于num=0和den=0的情况,我如何将testcalc设置为0(特别是因为我在最后一个用例之后删除了0!)?
https://stackoverflow.com/questions/505971
复制相似问题