当将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
*/
答案 0 :(得分:4)
显然,查询引擎为每一行计算两次随机数。
Col1
的第一次,第二次是持久列的CASE
语句。
优化器在这种情况下不知道或不关心NEWID
是非确定性函数并且调用它两次。
实际上,它甚至可能没有选择权。您是否希望优化器在后台创建临时表,使用生成随机数的表达式的结果填充其Col1
,然后读取该临时表并使用这些保存的中间结果来计算{{的结果1}}表达式,然后执行最终的CASE
?在这种情况下,优化器在不将中间结果写入磁盘的情况下计算表达式两次会更便宜。在其他一些情况下(例如,当您没有5个,但是50亿行或额外的索引时),估计的成本可能会有所不同,而且这种行为会发生变化。
我认为你不能做太多。请注意这种行为。始终将生成的随机数集明确保存到表中,然后根据它们执行进一步的计算。
我在SQL Server 2008和2014中重现了它。
这是我在SQL Server 2008中获得的执行计划,但它并不是很有趣。 2014年的计划是相同的,除了没有INSERT
运营商。
Top
运算符输出Constant Scan
列表,稍后会在Union1009
中使用。我想,它归结为Compute Scalar
和/或Constant Scan
运营商的实施细节。
观察到的行为告诉我们这里每行调用Compute Scalar
两次。
答案 1 :(得分:1)
在测试期间,我删除了与NEWID无关的功能,并在提前计算NEWID时显示结果。这可能对其他人有所帮助。
DECLARE @test TABLE (
InsertType VARCHAR(30),
Col1 VARCHAR(5),
Contains2 AS CASE WHEN (Col1) LIKE '%2%' THEN 1 ELSE 0 END) --depends on Col1
INSERT INTO @test (InsertType, Col1) VALUES
('Compute With Insert', LEFT(NEWID(), 5)),
('Compute With Insert', LEFT(NEWID(), 5)),
('Compute With Insert', LEFT(NEWID(), 5)),
('Compute With Insert', LEFT(NEWID(), 5)),
('Compute With Insert', LEFT(NEWID(), 5))
SELECT * FROM @test
DECLARE @A VARCHAR(5) = LEFT(NEWID(), 5);
DECLARE @B VARCHAR(5) = LEFT(NEWID(), 5);
DECLARE @C VARCHAR(5) = LEFT(NEWID(), 5);
DECLARE @D VARCHAR(5) = LEFT(NEWID(), 5);
DECLARE @E VARCHAR(5) = LEFT(NEWID(), 5);
SELECT @A, @B, @C, @D, @E;
INSERT INTO @Test (InsertType, Col1) VALUES
('Compute Before Insert', @A), ('Compute Before Insert', @B), ('Compute Before Insert', @C), ('Compute Before Insert', @D), ('Compute Before Insert', @E)
SELECT * FROM @test
InsertType Col1 Contains2
Compute With Insert C5507 0
Compute With Insert C17D7 0
Compute With Insert D9087 1
Compute With Insert E2DB0 0
Compute With Insert 7D1AF 1
Compute Before Insert 31050 0
Compute Before Insert 2954C 1
Compute Before Insert 9E205 1
Compute Before Insert DDF05 0
Compute Before Insert ED708 0