TIISNULL()
是惰性函数吗?
也就是说,如果我编写如下代码:
SELECT ISNULL(MYFIELD, getMyFunction()) FROM MYTABLE
它会始终评估getMyFunction()
还是仅在MYFIELD
实际为空的情况下评估它?
TIISNULL()
是惰性函数吗?
也就是说,如果我编写如下代码:
SELECT ISNULL(MYFIELD, getMyFunction()) FROM MYTABLE
它会始终评估getMyFunction()
还是仅在MYFIELD
实际为空的情况下评估它?
这工作正常
declare @X int
set @X = 1
select isnull(@X, 1/0)
但是引入聚合会使其失败,并证明有时可以在第一个参数之前评估第二个参数。
declare @X int
set @X = 1
select isnull(@X, min(1/0))
从不同的行为来看
SELECT ISNULL(1, 1/0)
SELECT ISNULL(NULL, 1/0)
第一个 SELECT 返回 1,第二个引发Msg 8134, Level 16, State 1, Line 4
Divide by zero error encountered.
错误。
您所指的这种“惰性”功能实际上称为“短路”
并且它并不总是有效,特别是如果您在 ISNULL 表达式中有一个 udf 。
查看这篇测试来证明它的文章:
短路(主要在 VB.Net 和 SQL Server 中)
T-SQL 是一种声明性语言,因此它无法控制用于获取结果的算法。它只是声明它需要什么结果。由查询引擎/优化器来确定具有成本效益的计划。在 SQL Server 中,优化器使用“矛盾检测”,它永远不会保证从左到右的评估,就像您在过程语言中假设的那样。
对于您的示例,进行了快速测试:
创建了标量值 UDF 以调用除以零错误:
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 的短路功能是安全的。
这是它认为最有效的方法。
现在它在功能上是惰性的,这很重要。例如,如果col1
是一个,当它为空时,varchar
它总是包含一个数字,那么col2
isnull(col2, cast(col1 as int))
将工作。
但是,没有指定它是否会在 null-check 之前或同时尝试强制转换,如果col2
不为 null 则吃掉错误,或者如果为 null 则只尝试col2
强制转换。
至少,我们希望它col1
在任何情况下都能获得,因为获得 2 个值的表的单次扫描将比每次获得一个值的两次扫描要快。
相同的 SQL 命令可以以非常不同的方式执行,因为我们给出的指令会根据对表的索引和统计信息的知识转换为较低级别的操作。
出于这个原因,就性能而言,答案是“当它看起来是一个好主意时,它是一个好主意,否则它不是”。
就观察到的行为而言,它是懒惰的。
编辑:Mikael Eriksson 的回答表明,由于不懒惰,有些情况可能确实会出错。就性能影响而言,我将在这里坚持我的答案,但至少在某些情况下,他的正确性影响至关重要。