为什么 COALESCE 会执行两次子查询?

Why does COALESCE execute the subquery twice?

我目前正在了解 ISNULL 和 COALESCE 之间的区别,并且我遇到了一个声明:

COALESCE((<subquery), 0)

翻译成(按照SQL标准):

CASE WHEN (<subquery>) IS NOT NULL THEN (<subquery>) ELSE 0 END

我的问题是:为什么子查询执行了两次?这似乎效率低下。

ISNULL() 与 SQL 服务器关联,所以这个问题似乎是关于 SQL 服务器的。正如评论中指出的那样,SQL 服务器 运行 两次子查询。

顺便说一下,这可能是有害的——而且不仅仅是子查询。考虑以下表达式:

select coalesce(case when rand(checksum(newid())) < 0.5 then 'a' end, 'b')

它可以 return 一个 NULL 值 -- 尽管 COALESCE(),因为第一个表达式被计算了两次。为了好玩,您可以 运行 这个查询:

select v.n, coalesce(case when rand(checksum(newid())) < 0.5 then 'a' end, 'b')
from (values (1), (2), (3), (4), (5), (6), (7), (8)) v(n);

我可以推测 SQL 服务器会这样运行的几个原因。

(1) Microsoft 或 Sybase 的某些人(曾几何时)实际上认为这是正确的方法。

(2) 有人认为"we already have a function that does this, so COALESCE() should be a little different"。即使那个"little difference"让它看起来像是坏了。

(3) SQL 服务器不会通过 运行 只优化子查询一次(据我所知)。因此,特别是对于子查询,想法可能是:"we'll fix this in a later round of optimization".

这都是猜测(因此也是观点)。我想回答,因为这不仅影响子查询。