Sql server Sql Server的ISNULL()函数是否延迟/短路?

Sql server Sql Server的ISNULL()函数是否延迟/短路?,sql-server,lazy-evaluation,short-circuiting,isnull,Sql Server,Lazy Evaluation,Short Circuiting,Isnull,这是一个惰性函数吗 也就是说,如果我编写如下代码: 从MYTABLE中选择ISNULLMYFIELD、getMyFunction 它将始终计算getMyFunction还是仅在MYFIELD实际为null的情况下计算它?从 SELECT ISNULL(1, 1/0) SELECT ISNULL(NULL, 1/0) 第一个选择返回1,第二个选择引发Msg 8134,级别16,状态1,第4行 遇到被零除的错误。错误。您所指的这个惰性功能实际上被称为短路 而且它并不总是有效的,特别是如果在ISN

这是一个惰性函数吗

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

从MYTABLE中选择ISNULLMYFIELD、getMyFunction


它将始终计算getMyFunction还是仅在MYFIELD实际为null的情况下计算它?

SELECT ISNULL(1, 1/0)

SELECT ISNULL(NULL, 1/0)
第一个选择返回1,第二个选择引发Msg 8134,级别16,状态1,第4行
遇到被零除的错误。错误。

您所指的这个惰性功能实际上被称为短路 而且它并不总是有效的,特别是如果在ISNULL表达式中有一个udf。 检查本文中运行测试以证明其正确性的地方:

T-SQL是一种声明性语言,因此它无法控制用于获取结果的算法。。它只是声明它需要什么结果。由查询引擎/优化器来制定经济高效的计划。在SQL Server中,优化器使用矛盾检测,这永远不会保证像您在过程语言中假设的那样从左到右进行计算

对于您的示例,您做了一个快速测试: 创建标量值UDF以调用除以零错误:

CREATE FUNCTION getMyFunction
( @MyValue INT )
RETURNS INT
AS
BEGIN
    RETURN (1/0)
END
GO
运行下面的查询并没有给我一个被零除的错误

将集合更改为下面的语句确实会给我带来被零除的错误。错误在ISNULL中引入了SELECT

但是对于值而不是变量,它从不给我错误

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检查之前还是同时执行强制转换,如果col2不为null,则会消除错误,或者如果col2为null,则只执行强制转换

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

同样的SQL命令可以以非常不同的方式执行,因为我们给出的指令是基于表的索引和统计信息转化为低级操作的

出于这个原因,就性能而言,答案是当它看起来是一个好主意时,它是,否则它不是

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

编辑:米凯尔·埃里克森的回答表明,有些情况确实可能因为不懒惰而出错。就性能影响而言,我将坚持我的答案,但就正确性影响而言,至少在某些情况下,他的答案是至关重要的。

这很好

declare @X int
set @X = 1
select isnull(@X, 1/0)
但引入一个聚合将使其失败,并证明第二个参数有时可以在第一个参数之前进行评估

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

你可以在函数中添加一个PRINT语句,然后自己去发现。@JarrettMeyer我小时候用这种技术学习编程,但现在不适用了,因为你不知道什么是实现细节,什么是记录的行为。学习变得越来越困难:只是说…@JarrettMeyer,打印在udf中是不可接受的。这并不完全可靠。子查询可能会由于不同的重写规则而导致问题。@usr,如果第一个字段始终为空,则不会导致出现错误情况。这并不是说服务器没有计算1/0,当它看到表达式没有被使用时就吃了这个错误——它可以自由地用相同的外部行为做任何它想做的事情。@Jonhana,是的,我的评论是针对潜在的性能问题的。@usr当然是双向的。对于IsNullcolA,colB如果仅仅因为它可能不需要而没有检索到colB,那将是非常糟糕的,因此最终会进行两次扫描。您很好地解释了这一事实,即它可能是懒惰的,也可能不是懒惰的,这取决于服务器的优化决策。谢谢,欢迎。不过我要补充一点,我想这个答案就是棺材上的钉子。现在我绝对不能指望它会短路。
declare @X int
set @X = 1
select isnull(@X, 1/0)
declare @X int
set @X = 1
select isnull(@X, min(1/0))