首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >COALESCE -保证短路?

COALESCE -保证短路?
EN

Stack Overflow用户
提问于 2009-02-03 04:00:00
回答 3查看 4.4K关注 0票数 11

this questiona neat answer about using COALESCE到简化复杂的逻辑树。我考虑了短路的问题。

例如,在大多数语言的函数中,参数都是经过充分计算的,然后传递给函数。在C中:

代码语言:javascript
运行
复制
int f(float x, float y) {
    return x;
}

f(a, a / b) ; // This will result in an error if b == 0

这似乎不是SQL Server中COALESCE“函数”的限制:

代码语言:javascript
运行
复制
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时,我预计会看到如下错误:

代码语言:javascript
运行
复制
Msg 8134, Level 16, State 1, Line 1
Divide by zero error encountered.

我找到了一些甲骨文的mentions related。以及SQL Server的一些测试。当您包含用户定义的函数时,短路可能会中断。

那么,这种行为应该得到ANSI标准的保证吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2009-02-03 04:40:22

我刚刚看了一下链接的文章,可以确认COALESCE和ISNULL的短路都会失败。

如果涉及任何子查询,它似乎都会失败,但它对于标量函数和硬编码值很有效。

例如,

代码语言:javascript
运行
复制
DECLARE @test INT
SET @test = 1
PRINT 'test2'
SET @test = COALESCE(@test, (SELECT COUNT(*) FROM sysobjects))
SELECT 'test2', @test
-- OUCH, a scan through sysobjects

COALESCE是根据ANSI standard实现的。它只是CASE语句的简写。ISNULL不是ANSI标准的一部分。第6.9节似乎没有显式地要求短路,但它确实意味着应该返回when语句中的第一个true子句。

下面是一些适用于基于标量的函数的证明(我是在SQL Server 2005上运行的):

代码语言:javascript
运行
复制
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的底层实现将执行子查询的一些证据。

代码语言:javascript
运行
复制
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.
票数 8
EN

Stack Overflow用户

发布于 2009-02-03 05:08:24

在MS SQL Server中保证短路的有效方法是用例。对于成功的WHEN子句,不计算其他子句。

COALESCE can have issues

在本例中,为什么在COALESCE/CASE构造中有这么多分支?

代码语言:javascript
运行
复制
SELECT Numerator
    ,Denominator
    ,CASE
        WHEN Denominator = 0 THEN 0 END,
        ELSE Numerator / Denominator
     END AS TestCalc
FROM Fractions
票数 3
EN

Stack Overflow用户

发布于 2009-02-03 04:21:27

我也很惊讶地看到答案是有效的!我不确定这种行为是不是有保证。(但我找不到一个不起作用的例子!)

SQL工作了五年,我仍然很惊讶。

我还继续做了一个更改:

代码语言:javascript
运行
复制
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

我得到的结果是:

代码语言:javascript
运行
复制
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!)?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/505971

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档