Minha consulta de trabalho real era uma junção interna, mas este exemplo simples com junção cruzada parece quase sempre reproduzir o problema.
SELECT *
FROM (
SELECT 1 UNION ALL
SELECT 2
) AA ( A )
CROSS JOIN (
SELECT NEWID() TEST_ID
) BB ( B )
Com minha junção interna, eu tinha muitas linhas para as quais adicionei a cada um um GUID usando a função NEWID () e, para cerca de 9 de 10 dessas linhas, a multiplicação com a tabela virtual de 2 linhas produziu os resultados esperados, apenas 2 cópias de o mesmo GUID, enquanto 1 em 10 produziria resultados diferentes. Isso foi inesperado para dizer o mínimo e me deu muita dificuldade tentando encontrar esse bug no meu script de geração de dados de teste.
Se você der uma olhada nas consultas a seguir usando também as funções getdate e sysdatetime não determinísticas, não verá isso, de qualquer maneira - sempre vejo o mesmo valor de data e hora em ambas as linhas do resultado final.
SELECT *
FROM (
SELECT 1 UNION ALL
SELECT 2
) AA ( A )
CROSS JOIN (
SELECT GETDATE() TEST_ID
) BB ( B )
SELECT *
FROM (
SELECT 1 UNION ALL
SELECT 2
) AA ( A )
CROSS JOIN (
SELECT SYSDATETIME() TEST_ID
) BB ( B )
Atualmente, estou usando o SQL Server 2008 e meu trabalho por enquanto é carregar minhas linhas com GUIDs em uma variável de tabela antes de terminar meu script de geração de dados aleatórios. Depois de tê-los como valores em uma tabela em vez de uma tabela virtual, o problema desaparece.
Eu tenho uma solução alternativa, mas estou procurando maneiras de contornar sem tabelas reais ou variáveis de tabela.
Enquanto escrevia isso, tentei sem sucesso estas possibilidades: 1) colocar o newid () em uma tabela virtual aninhada:
SELECT *
FROM (
SELECT 1 UNION ALL
SELECT 2
) AA ( A )
CROSS JOIN (
SELECT TEST_ID
FROM (
SELECT NEWID() TEST_ID
) TT
) BB ( B )
2) envolvendo o newid() dentro de uma expressão de conversão, como:
SELECT CAST(NEWID() AS VARCHAR(100)) TEST_ID
3) invertendo a ordem de aparecimento das tabelas virtuais dentro da expressão join
SELECT *
FROM (
SELECT NEWID() TEST_ID
) BB ( B )
CROSS JOIN (
SELECT 1 UNION ALL
SELECT 2
) AA ( A )
4) usando aplicação cruzada não correlacionada
SELECT *
FROM (
SELECT NEWID() TEST_ID
) BB ( B )
CROSS APPLY (
SELECT 1 UNION ALL
SELECT 2
) AA ( A )
Pouco antes de finalmente postar esta pergunta, agora tentei com sucesso, ao que parece, uma aplicação cruzada correlacionada:
SELECT *
FROM (
SELECT NEWID() TEST_ID
) BB ( B )
CROSS APPLY (
SELECT A
FROM (
SELECT 1 UNION ALL
SELECT 2
) TT ( A )
WHERE BB.B IS NOT NULL
) AA ( A )
Alguém tem alguma outra solução alternativa mais elegante e simples? Eu realmente não quero usar aplicação cruzada ou correlação para uma multiplicação de linha simples, se não for necessário.
Esse comportamento é intencional, conforme explicado em detalhes neste relatório de bug do Connect . A resposta mais pertinente da Microsoft é reproduzida abaixo por conveniência (e caso o link morra em algum momento):
As funções
GETDATE
eSYSDATETIME
são de fato não determinísticas, mas são tratadas como constantes de tempo de execução para uma consulta específica. Em termos gerais, isso significa que o valor da função é armazenado em cache quando a execução da consulta é iniciada e o resultado é reutilizado para todas as referências na consulta.Nenhuma das 'soluções alternativas' na questão é segura; não há garantia de que o comportamento não mudará na próxima vez que o plano for compilado, na próxima vez que você aplicar um service pack ou atualização cumulativa... ou por outros motivos.
A única solução segura é usar algum tipo de objeto temporário - uma variável, tabela ou função de várias instruções, por exemplo. Usar uma solução alternativa que parece funcionar hoje com base na observação é uma ótima maneira de experimentar comportamentos inesperados no futuro, geralmente na forma de um alerta de pager às 3h da manhã de domingo.