Performance 为什么IsNull的速度是coalesce(相同查询)的两倍?

Performance 为什么IsNull的速度是coalesce(相同查询)的两倍?,performance,sql-server-2008,isnull,Performance,Sql Server 2008,Isnull,我们在SQLServer2008(SP1)-10.0.2531.0(X64)-Win2008SP2(X64)上遇到了一个奇怪的情况 这里有一个很难回答的问题: select t1.id, t2.id from t1, t2 where t1.id = t2.ext_id and isnull(t1.vchCol1, 'Null') = isnull(t2.vchCol1, 'Null') and isnull(t1.vchCol2, 'Null') = isnu

我们在SQLServer2008(SP1)-10.0.2531.0(X64)-Win2008SP2(X64)上遇到了一个奇怪的情况

这里有一个很难回答的问题:

select t1.id, t2.id 
from t1, t2
where 
     t1.id = t2.ext_id
     and isnull(t1.vchCol1, 'Null') = isnull(t2.vchCol1, 'Null')
     and isnull(t1.vchCol2, 'Null') = isnull(t2.vchCol2, 'Null')
     .... and about 10 more comparisons with Isnull
UPD:比较中的所有列(ID除外)都是
varchar
(~30…200)
T1约为130mln行,T2约为300k行

这些查询在相当大的Dev服务器上运行~5个小时——这很慢,但我们能做什么呢

虽然我们研究了可能的优化方法-我们发现,在上面的查询中将“isnull”更改为“coalesce”,可以获得双倍的性能增益-并且查询现在运行了~2个小时

UPD:当我们删除所有
ISNULL
检查并仅使用
t1.vchCol1=t2.vchCol1
时,查询在40分钟后完成


问题是:这是已知的行为吗?我们应该避免在任何地方使用IsNull吗?

关于这个主题的大多数文章似乎都与此相矛盾
ISNULL
COALESCE
快一点

合并
基本上转化为
案例
表达式和
ISNULL
是内置的 在数据库引擎中实现。

这将是一场表演 与
合并的差异和查询
这里的情况往往更糟

我在一台计算机上运行了几次这些测试 几乎没有不同的服务器,
ISNULL
看起来相当一致 平均执行
合并

10%或12%。但这就是问题所在
6秒和5.3秒之间的差值
秒(近似平均值)
每个测试在我的服务器上的运行时间),超过
一百万次处决的过程。
几乎不值得拥有这些功能和功能
标准符合性牺牲,在
至少在我使用的场景中
功能

最好的执行者是
为空或
大小写, 而这三者之间的差异 它们是次要的


我想知道您是否会通过明确地将案例拆分出来看到改进:

...
AND ((t1.vchCol1 = t2.vchCol1) OR (t1.vchCol1 IS NULL AND t2.vchCol1 IS NULL))
AND ((t1.vchCol2 = t2.vchCol2) OR (t1.vchCol2 IS NULL AND t2.vchCol2 IS NULL))
...

您可能想考虑在包含校验和值的每个表中添加一个计算列。然后,在ID列和校验和值上创建索引,最后在联接中使用校验和值。大概是这样的:

Alter Table T1 Add CheckSumId As CHECKSUM(vchCol1, vchCol2, vchCol3)
Alter Table T2 Add CheckSumId As CHECKSUM(vchCol1, vchCol2, vchCol3)

Create NonClustered index idx_T1_Checksum On T1(id, CheckSumId)
Create NonClustered index idx_T2_Checksum On T2(ext_id, CheckSumId)
那么您的查询将变成

select t1.id, t2.id 
from t1 Inner Join t2
       On t1.id = t2.ext_id
       And T1.CheckSumId = T2.CheckSumId
where  isnull(t1.vchCol1, 'Null') = isnull(t2.vchCol1, 'Null')
     and isnull(t1.vchCol2, 'Null') = isnull(t2.vchCol2, 'Null')

当然,这将使用额外的索引空间,但它只是2个整数,这应该是非常有效的。由于需要维护另一个索引,因此每次插入、更新和删除都会有性能损失。但是,我怀疑这会对性能产生很大影响。

我意识到这是一年后的事,但是

对于这种按列比较,您可以考虑使用除此之外的。另外,EXCEPT将NULL视为另一个值,而不是我喜欢称之为“它可以是任何东西!”

在比较行以确定不同的值时,两个空值被视为相等
--从

什么数据类型是vchCol1等?我认为
(t1.vchCol1=t2.vchCol1)
比运行函数(喜欢强制表扫描)在性能上更好,但它需要将
ANSI_NULLS
设置为
关闭
。你看过查询执行计划了吗?我肯定你对此无能为力,但这只是空值是邪恶的另一个原因@zmische-你找到了两者之间差异的根本原因了吗?但在我们的案例中,在切换到凝聚后,我们得到了从5小时到2小时的提升。原因是什么?对于我来说-ISNULL应该更快,这就是为什么我如此困惑的原因。只需添加,在
ISNULL(子查询,X)
COALESCE(子查询,X)
的情况下,第二个会对子查询求值两次。同意。这种模式。在查看链接答案时(与参数相比与另一列略有不同),我发现它在计划中只是一个简单的等式。因此您认为这是正常行为,
coalesce
Isnull
快2倍。我们所能做的就是拆分案例,以便SQL清楚地了解如何进行优化?因此,不应将其视为SQL Server中的性能缺陷?@zmische-我会认为
coalesce
isnull
表达式都是无法组织且不理想的。不知道为什么您会看到两者在性能上的差异。计划有什么不同?其中一个是否比另一个具有更精确的行估计?哈希联接中表的顺序相同还是相反?关于逻辑读取和CPU时间,
将统计信息IO设置为ON
将统计时间设置为ON
说明了什么?可能在
ISNULL
版本中,由于估计值不同,menory授权不足以进行散列联接。@Martin-thx用于提问。我没有确切的统计数据-另一个团队正在使用该服务器。但我会在下一次运行中尝试解决这个问题。@zmische-我认为内存不足和散列溢出是很可能的。您应该能够从估计的计划中看到这一点,而不需要通过查看估计的行数和行大小来运行整个查询。