sql - COALESCE - 保证短路吗?

标签 sql sql-server tsql coalesce short-circuiting

来自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

如果它在分母 = 0 时评估第二种情况,我预计会看到如下错误:

Msg 8134, Level 16, State 1, Line 1
Divide by zero error encountered.

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

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

最佳答案

我刚刚查看了链接的文章,可以确认短路对于 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 sysobjects

COALESCE 是根据 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.

关于sql - COALESCE - 保证短路吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/505971/

相关文章:

MySQL存储过程,处理多个游标和查询结果

sql - 查询以获取前一周销售额排名最高的产品

php - 代表论坛网站泛化案例的最佳方法是什么

SQL 删除行返回 - "ORA-02292: integrity constraint (..) violated - child record found"

python - 使用 pymssql 将数据插入 SQL Server 表

sql-server-2008 - 从 varchar 字段中删除单个字符 SQL Server 2008

sql - 将四列插入一列

mysql - 选择在此位置无效,期望 (

SQL - 连续 "ON"语句

sql-server - 获取 12 月 31 日的正确周数