O SQL Server pode avaliar A <> B
como A < B OR A > B
, mesmo que uma das expressões seja não determinística?
Este é um ponto um tanto controverso, e a resposta é um "sim" qualificado.
A melhor discussão que eu conheço foi dada em resposta ao relatório de bug do Itzik Ben-Gan do Connect Bug with NEWID e Table Expressions , que foi fechado como não será corrigido. O Connect foi desativado, então o link existe para um arquivo da web. Infelizmente, muito material útil foi perdido (ou dificultado de encontrar) pelo fim do Connect. De qualquer forma, as citações mais úteis de Jim Hogg da Microsoft são:
Isso atinge o cerne da questão - a otimização pode alterar a semântica de um programa? Ou seja: se um programa fornece certas respostas, mas é executado lentamente, é legítimo que um Query Optimizer faça com que o programa seja executado mais rapidamente, mas também altera os resultados fornecidos?
Antes de gritar "NÃO!" (minha própria inclinação pessoal também :-), considere: a boa notícia é que, em 99% dos casos, as respostas são as mesmas. Portanto, a otimização de consultas é uma vitória clara. A má notícia é que, se a consulta contiver código com efeito colateral, planos diferentes PODEM realmente produzir resultados diferentes. E NEWID () é uma dessas 'funções' com efeito colateral (não determinística) que expõe a diferença. [Na verdade, se você experimentar, poderá criar outras - por exemplo, avaliação de curto-circuito das cláusulas AND: faça a segunda cláusula gerar uma divisão aritmética por zero - otimizações diferentes podem executar essa segunda cláusula ANTES da primeira cláusula] Isso reflete A explicação de Craig, em outra parte deste segmento, que o SqlServer não garante quando os operadores escalares são executados.
Portanto, temos uma escolha: se queremos garantir um certo comportamento na presença de código não determinístico (efeito colateral) - para que os resultados de JOINs, por exemplo, sigam a semântica de uma execução de loop aninhado - então nós pode usar OPÇÕES apropriadas para forçar esse comportamento - como aponta a UC. Mas o código resultante ficará lento - esse é o custo de, de fato, prejudicar o Query Optimizer.
Tudo isso dito, estamos movendo o Query Optimizer na direção do comportamento "conforme o esperado" para NEWID () - trocando desempenho por "resultados conforme o esperado".
Um exemplo da mudança de comportamento nesse sentido ao longo do tempo é o NULLIF funciona incorretamente com funções não determinísticas, como RAND () . Também existem outros casos semelhantes usando, por exemplo, COALESCE
uma subconsulta que pode produzir resultados inesperados e que também estão sendo tratados gradualmente.
Jim continua:
Fechando o loop. . . Eu discuti essa questão com a equipe de desenvolvimento. E, finalmente, decidimos não mudar o comportamento atual, pelos seguintes motivos:
1) O otimizador não garante tempo ou número de execuções de funções escalares. Esse é um princípio estabelecido há muito tempo. É a 'margem de manobra' fundamental que permite ao otimizador liberdade suficiente para obter melhorias significativas na execução do plano de consulta.
2) Esse "comportamento de uma vez por linha" não é um problema novo, embora não seja amplamente discutido. Começamos a ajustar seu comportamento de volta no lançamento do Yukon. Mas é muito difícil definir com precisão, em todos os casos, exatamente o que isso significa! Por exemplo, isso se aplica a linhas intermediárias calculadas 'a caminho' do resultado final? - nesse caso, depende claramente do plano escolhido. Ou se aplica apenas às linhas que aparecerão no resultado concluído? - Há uma recursão desagradável acontecendo aqui, como eu tenho certeza que você concorda!
3) Como mencionei anteriormente, o padrão é "otimizar o desempenho" - o que é bom para 99% dos casos. O 1% dos casos em que isso pode alterar os resultados é bastante fácil de detectar - 'funções' com efeito colateral, como NEWID - e fácil de 'consertar' (desempenho da negociação, como conseqüência). Esse padrão para "otimizar o desempenho" novamente, é estabelecido há muito tempo e aceito. (Sim, não é a postura escolhida pelos compiladores para linguagens de programação convencionais, mas que seja).
Portanto, nossas recomendações são:
a) Evite confiar no tempo não garantido e na semântica do número de execuções. b) Evite usar NEWID () nas expressões da tabela. c) Use OPTION para forçar um comportamento específico (desempenho da negociação)
Espero que esta explicação ajude a esclarecer nossas razões para fechar esse bug como "não será corrigido".
Curiosamente, AND NOT (s_guid = NEWID())
produz o mesmo plano de execução
Isso é consequência da normalização, que ocorre muito cedo durante a compilação de consultas. Ambas as expressões são compiladas exatamente da mesma forma normalizada, para que o mesmo plano de execução seja produzido.