使用NEWID()和PERSISTED计算列的结果不一致

Kri*_*fer 6 t-sql sql-server case calculated-columns newid

当将NEWID()与持久计算列结合使用时,我得到奇怪的结果.我使用了一些功能错了吗?

在创建列时不使用持久化,因此在选择它们时计算值将返回正确的值.更新列(col1)也将返回正确的值.

DECLARE @test TABLE (
    Col1 INT,
    Contains2 AS CASE WHEN 2 IN (Col1) THEN 1 ELSE 0 END PERSISTED)

INSERT INTO @test (Col1) VALUES
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5))

SELECT * FROM @test
UPDATE @test SET Col1 = Col1*1
SELECT * FROM @test

/*
Col1    Contains2
2   0
2   0
0   1
4   0
3   0

Col1    Contains2
2   1
2   1
0   0
4   0
3   0
*/
Run Code Online (Sandbox Code Playgroud)

Vla*_*nov 4

显然,查询引擎为每行计算两次随机数。

第一次用于Col1,第二次用于CASE持久化列的语句。

优化器不知道,或者在这种情况下不关心这NEWID是一个非确定性函数,并调用它两次。

事实上,它甚至可能没有选择。您是否希望优化器在幕后创建一个临时表,用Col1生成随机数的表达式的结果填充其,然后读回该临时表并使用这些保存的中间结果来计算表达式的结果CASE,然后执行最终的结果INSERT?在这种情况下,优化器计算表达式两次而不将中间结果写入磁盘会更便宜。在其他一些情况下(例如,当您没有 5 行,而是 50 亿行或额外的索引时),估计成本可能会有所不同,并且这种行为会发生变化。

我认为你对此无能为力。只要注意这种行为即可。始终将生成的随机数集显式保存到表中,然后根据它们执行进一步的计算。

我在 SQL Server 2008 和 2014 中重现了它。这是我在 SQL Server 2008 中获得的执行计划,但它并不是很有趣。2014 年的计划是相同的,只是没有Top运营商。

2008年计划

Constant Scan运算符输出一个Union1009列表,稍后使用Compute Scalar。我想,这取决于Constant Scan和/或Compute Scalar运算符的实现细节。

观察到的行为告诉我们,newid()这里每行被调用两次。

  • 在相关帖子中,用户 Martin Smith 评论道“顺便说一句,已经在这里报告了” https://connect.microsoft.com/SQLServer/Feedback/Details/2751288 (2认同)