Estou acompanhando esta questão sobre valores estranhos em uma PERSISTED
coluna computada. A resposta faz algumas suposições sobre como esse comportamento surgiu.
Estou perguntando o seguinte: isso não é um bug definitivo? As PERSISTED
colunas podem se comportar dessa maneira?
DECLARE @test TABLE (
Col1 INT,
Contains2 AS CASE WHEN 2 IN (Col1) THEN 1 ELSE 0 END PERSISTED) --depends on Col1
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 --shows impossible data
UPDATE @test SET Col1 = Col1*1 --"fix" the data by rewriting it
SELECT * FROM @test --observe fixed data
/*
Col1 Contains2
2 0
2 0
0 1
4 0
3 0
Col1 Contains2
2 1
2 1
0 0
4 0
3 0
*/
Observe que os dados parecem "impossíveis" porque os valores da coluna calculada não correspondem à sua definição.
É bem conhecido que funções não determinísticas em consultas podem se comportar de maneira estranha, mas aqui isso parece violar o contrato de colunas computadas persistentes e, portanto, deveria ser ilegal.
Inserir números aleatórios pode ser um cenário artificial, mas e se estivéssemos inserindo NEWID()
valores ou SYSUTCDATETIME()
? Acho que essa é uma questão relevante que pode se manifestar de forma prática.
Isso é certamente um bug. O fato de os
col1
valores serem o resultado de uma expressão envolvendo números aleatórios claramente não altera o que o valor corretocol2
deveria ser.DBCC CHECKDB
retorna um erro se for executado em uma tabela permanente.Dá (para o meu teste que tinha uma linha "impossível")
Também informa que
E, se for escolhida a opção de reparo, exclui sem cerimônia toda a linha, pois não há como saber qual coluna está corrompida.
Anexar um depurador mostra que
NEWID()
está sendo avaliado duas vezes por linha inserida. Uma vez antes daCASE
expressão ser avaliada e uma vez dentro dela.Uma possível solução alternativa pode ser usar
O que, por um motivo ou outro, evita o problema e avalia a expressão apenas uma vez por linha.
De acordo com a conversa do comentário, o consenso parece ser que a resposta à pergunta do OP é que isso constitui um bug (ou seja, deveria ser ilegal).
O OP faz referência à análise de Vladimir Baranov no StackOverflow, onde eles afirmam:
"Primeira vez para Col1, segunda vez para a instrução CASE da coluna persistente.
O otimizador não sabe ou não se importa nesse caso que NEWID é uma função não determinística e a chama duas vezes."
Dito de outra forma, deve-se esperar que [o NEWID() dentro] col1 tenha o mesmo valor que você acabou de inserir quando fez o cálculo.
Isso seria sinônimo do que está acontecendo com o bug, onde NEWID é criado para Col1 e depois criado novamente para a coluna persistida:
Em meus testes, outras funções não determinísticas como RAND e valores de tempo não resultaram no mesmo bug.
De acordo com Martin, isso foi enviado à Microsoft ( https://connect.microsoft.com/SQLServer/Feedback/Details/2751288 ), onde há comentários nesta página e na análise do StackOverflow (abaixo).