Sql Server的ISNULL()函数是懒惰/短路的吗?

时间:2012-08-31 16:40:11

标签: sql-server lazy-evaluation short-circuiting isnull

TI ISNULL()一个懒惰的功能?

也就是说,如果我编写如下代码:

SELECT ISNULL(MYFIELD, getMyFunction()) FROM MYTABLE

它会始终评估getMyFunction()还是只会在MYFIELD实际为空的情况下评估它?

4 个答案:

答案 0 :(得分:4)

这很好用

declare @X int
set @X = 1
select isnull(@X, 1/0)

但是引入一个聚合会使它失败,并证明第二个参数可以在第一个参数之前进行评估,有时候。

declare @X int
set @X = 1
select isnull(@X, min(1/0))

答案 1 :(得分:3)

的不同行为来判断
SELECT ISNULL(1, 1/0)

SELECT ISNULL(NULL, 1/0)

第一个SELECT返回1,第二个SELECT引发Msg 8134, Level 16, State 1, Line 4 Divide by zero error encountered.错误。

答案 2 :(得分:2)

您所指的这种“懒惰”功能实际上称为“短路” 并且它并不总是有效,特别是如果你在ISNULL表达式中有一个udf 查看本文运行测试的文章以证明它:
Short-circuiting (mainly in VB.Net and SQL Server)

T-SQL是一种声明性语言,因此无法控制用于获取结果的算法。它只是声明了它需要的结果。查询引擎/优化器可以找出具有成本效益的计划。在SQL Server中,优化器使用“矛盾检测”,它永远不会保证从程序语言中假设的从左到右的评估。


对于你的例子,做了一个快速测试:
创建标量值UDF以调用Divide by zero错误:

CREATE FUNCTION getMyFunction
( @MyValue INT )
RETURNS INT
AS
BEGIN
    RETURN (1/0)
END
GO

运行以下查询时没有给我Divide by zero error encountered错误。

DECLARE @test INT
SET @test = 1
SET @test = ISNULL(@test, (dbo.getMyFunction(1)))
SELECT @test

SET更改为以下语句确实给出了Divide by zero error encountered.错误。 (在SELECT)中引入ISNULL

SET @test = ISNULL(@test, (SELECT dbo.getMyFunction(1)))

但是使用值而不是变量,它从未给我错误。

SELECT ISNULL(1, (dbo.getMyFunction(1)))
SELECT ISNULL(1, (SELECT dbo.getMyFunction(1)))

因此,除非您真正弄清楚优化器如何针对所有排列评估这些表达式,否则不依赖于T-SQL的短路功能是安全的。

答案 3 :(得分:2)

它认为哪种方式效果最好。

现在它在功能上很懒,这是重要的事情。例如。如果col1是一个varchar,当col2为空时,它将始终包含一个数字,那么

isnull(col2, cast(col1 as int))

会工作。

但是,没有指定它是否会在null检查之前或同时尝试强制转换,如果col2不为空,则会吃掉错误,或者只有col2才会尝试强制转换{} 1}}为空。

至少,我们希望它在任何情况下都能获得col1,因为获得2个值的表的单次扫描将比两次扫描获得一个更快。

相同的SQL命令可以以非常不同的方式执行,因为我们给出的指令基于对索引的知识和关于表的统计信息而变成了低级操作。

出于这个原因,就性能而言,答案是“当它似乎是一个好主意,否则就不是”。

就观察到的行为而言,它很懒惰。

编辑:Mikael Eriksson的回答显示,由于不是懒惰,有些情况可能确实是错误的。在性能影响方面我会坚持我的答案,但至少在某些情况下,他对正确性影响至关重要。