Encontramos um problema interessante com o SQL Server. Considere o seguinte exemplo de reprodução:
CREATE TABLE #test (s_guid uniqueidentifier PRIMARY KEY);
INSERT INTO #test (s_guid) VALUES ('7E28EFF8-A80A-45E4-BFE0-C13989D69618');
SELECT s_guid FROM #test
WHERE s_guid = '7E28EFF8-A80A-45E4-BFE0-C13989D69618'
AND s_guid <> NEWID();
DROP TABLE #test;
Por favor, esqueça por um momento que a s_guid <> NEWID()
condição parece totalmente inútil - este é apenas um exemplo mínimo de reprodução. Como a probabilidade de NEWID()
corresponder a um determinado valor constante é extremamente pequena, ela deve ser avaliada como TRUE todas as vezes.
Mas não. A execução dessa consulta geralmente retorna 1 linha, mas às vezes (com bastante frequência, mais de 1 vez em 10) retorna 0 linhas. Eu o reproduzi com o SQL Server 2008 no meu sistema e você pode reproduzi-lo on-line com o violino vinculado acima (SQL Server 2014).
Observar o plano de execução revela que o analisador de consultas aparentemente divide a condição em s_guid < NEWID() OR s_guid > NEWID()
:
...o que explica completamente por que às vezes falha (se o primeiro ID gerado for menor e o segundo maior que o ID fornecido).
O SQL Server tem permissão para avaliar A <> B
como A < B OR A > B
, mesmo se uma das expressões não for determinística? Se sim, onde está documentado? Ou encontramos um bug?
Curiosamente, AND NOT (s_guid = NEWID())
produz o mesmo plano de execução (e o mesmo resultado aleatório).
Encontramos esse problema quando um desenvolvedor queria excluir opcionalmente uma linha específica e usou:
s_guid <> ISNULL(@someParameter, NEWID())
como um "atalho" para:
(@someParameter IS NULL OR s_guid <> @someParameter)
Estou procurando documentação e/ou confirmação de um bug. O código não é tão relevante, portanto, não são necessárias soluções alternativas.
Este é um ponto um tanto controverso, e a resposta é um "sim" qualificado.
A melhor discussão que conheço foi dada em resposta ao relatório de bug do Connect de Itzik Ben-Gan Bug with NEWID and Table Expressions , que foi fechado porque não será corrigido. O Connect foi aposentado, então o link é para um arquivo da web. Infelizmente, muito material útil foi perdido (ou ficou mais difícil de encontrar) com o fim do Connect. De qualquer forma, as citações mais úteis de Jim Hogg da Microsoft são:
Um exemplo da mudança de comportamento a esse respeito ao longo do tempo é que NULLIF funciona incorretamente com funções não determinísticas, como RAND() . Existem também outros casos semelhantes usando, por exemplo,
COALESCE
uma subconsulta que pode produzir resultados inesperados e que também estão sendo abordados gradualmente.Jim continua:
Isso é uma consequência da normalização, que acontece muito cedo durante a compilação da consulta. Ambas as expressões compilam exatamente na mesma forma normalizada, de modo que o mesmo plano de execução é produzido.
Isso está documentado (mais ou menos) aqui:
Funções definidas pelo usuário
Este não é o único formulário de consulta em que o plano de consulta executará NEWID() várias vezes e alterará o resultado. Isso é confuso, mas é realmente crítico para NEWID() ser útil para geração de chaves e classificação aleatória.
O mais confuso é que nem todas as funções não determinísticas realmente se comportam assim. Por exemplo, RAND() e GETDATE() serão executados apenas uma vez por consulta.
Por que vale a pena, se você olhar para este antigo documento padrão SQL 92 , os requisitos em torno da desigualdade são descritos na seção "
8.2 <comparison predicate>
" da seguinte forma:Nota: Eu incluí 7b e 7h para completar, pois eles falam sobre
<>
comparação - não acho que a comparação de construtores de valor de linha com vários valores seja implementada em T-SQL, a menos que eu esteja apenas entendendo mal o que isso diz - o que é bem possívelIsso é um monte de lixo confuso. Mas se você quiser continuar mergulhando no lixo...
Acho que 1.ii é o item que se aplica nesse cenário, já que estamos comparando os valores de "elementos construtores de valor de linha".
Basicamente está dizendo que
X <> Y
é verdade se os valores representados por X e Y não forem iguais. ComoX < Y OR X > Y
é uma reescrita logicamente equivalente desse predicado, é totalmente legal para o otimizador usar isso.O padrão não coloca nenhuma restrição nessa definição relacionada à determinística (ou qualquer outra coisa, você entende) dos elementos construtores de valor de linha em ambos os lados do
<>
operador de comparação. É responsabilidade do código do usuário lidar com o fato de que uma expressão de valor em um lado pode ser não determinística.