Estou usando uma COALESCE
função T-SQL em que o primeiro argumento não será nulo em cerca de 95% das vezes que é executado. Se o primeiro argumento for NULL
, o segundo argumento é um processo bastante demorado:
SELECT COALESCE(c.FirstName
,(SELECT TOP 1 b.FirstName
FROM TableA a
JOIN TableB b ON .....)
)
Se, por exemplo, c.FirstName = 'John'
o SQL Server ainda executasse a subconsulta?
Eu sei com a IIF()
função VB.NET , se o segundo argumento for True, o código ainda lê o terceiro argumento (mesmo que não seja usado).
fonte
CASE
sempre avalia os circuitos da esquerda para a direita e sempre os curtos )SELECT COALESCE((SELECT CASE WHEN RAND() <= 0.5 THEN 1 END), 1);
- repetir várias vezes. Você receberáNULL
algumas vezes. Tente novamente comISNULL
- você nunca conseguiráNULL
...Que tal esse - como me foi relatado por Itzik Ben-Gan, que Jaime Lafargue falou sobre isso ?
Resultado:
Existem soluções triviais, é claro, mas o ponto ainda é que
CASE
nem sempre garante a avaliação / curto-circuito da esquerda para a direita. Eu relatei o bug aqui e ele foi fechado como "por design". Paul White posteriormente arquivou este item do Connect e foi fechado como Fixo. Não porque foi corrigido per se, mas porque eles atualizaram os Manuais Online com uma descrição mais precisa do cenário em que agregados podem alterar a ordem de avaliação de umaCASE
expressão. Eu escrevi recentemente mais sobre isso aqui .EDITE apenas um adendo, embora eu concorde que esses são casos extremos, que na maioria das vezes você pode confiar na avaliação da esquerda para a direita e em curto-circuito, e que esses são bugs que contradizem a documentação e provavelmente serão corrigidos ( isso não é definitivo - veja a conversa de acompanhamento no post de Bart Duncan no blog para saber o porquê). Tenho que discordar quando as pessoas dizem que algo sempre é verdadeiro, mesmo que exista um caso único que o refute. Se Itzik e outros podem encontrar bugs solitários como esse, torna pelo menos no campo da possibilidade que existem outros bugs também. E como não sabemos o restante da consulta do OP, não podemos dizer com certeza que ele confiará nesse curto-circuito, mas acabará sendo mordido por ele. Então, para mim, a resposta mais segura é:
Enquanto você pode geralmente dependem de
CASE
avaliar esquerda para a direita e de curto-circuito, conforme descrito na documentação, não é preciso dizer que você pode sempre fazê-lo. Há dois casos demonstrados nesta página em que isso não é verdade e nenhum bug foi corrigido em nenhuma versão publicamente disponível do SQL Server.EDIT aqui é outro caso (eu preciso parar de fazer isso) em que uma
CASE
expressão não é avaliada na ordem que você esperaria, mesmo que nenhum agregado esteja envolvido.fonte
CASE
que foi resolvido silenciosamenteMinha opinião sobre isso é que a documentação deixa razoavelmente claro que a intenção é que o CASE entre em curto-circuito. Como Aaron menciona, houve vários casos (ha!) Em que isso demonstrou que nem sempre é verdade.
Até o momento, tudo isso foi reconhecido como bugs e corrigido - embora não necessariamente em uma versão do SQL Server que você possa comprar e consertar hoje (o bug de dobragem constante ainda não chegou ao AFAIK de atualização cumulativa). O novo bug em potencial - relatado originalmente por Itzik Ben-Gan - ainda não foi investigado (Aaron ou eu o adicionaremos ao Connect em breve).
Relacionadas à pergunta original, há outros problemas com CASE (e, portanto, COALESCE), em que funções ou subconsultas de efeito colateral são usadas. Considerar:
O formulário COALESCE geralmente retorna NULL, mais detalhes em https://connect.microsoft.com/SQLServer/feedback/details/546437/coalesce-subquery-1-may-return-null
Os problemas demonstrados com transformador otimizador e rastreamento de expressão comum significam que é impossível garantir que o CASE entre em curto-circuito em todas as circunstâncias. Posso conceber casos em que talvez nem seja possível prever o comportamento inspecionando a saída do plano de exibição pública, embora eu não tenha uma reprovação para isso hoje.
Em resumo, acho que você pode estar razoavelmente confiante de que o CASE entrará em curto-circuito em geral (principalmente se uma pessoa razoavelmente qualificada inspecionar o plano de execução e esse plano de execução for 'aplicado' com um guia ou dicas), mas se você precisar uma garantia absoluta, você deve escrever SQL que não inclua a expressão.
Não é uma situação extremamente satisfatória, eu acho.
fonte
Já deparei com outro caso em que
CASE
/COALESCE
não provo um curto-circuito. O TVF a seguir apresentará uma violação de PK se for passado1
como parâmetro.Se chamado da seguinte maneira
Ou como
Ambos dão o resultado
mostrando que a
SELECT
(ou pelo menos a população variável da tabela) ainda é executada e gera um erro, mesmo que esse ramo da instrução nunca deva ser alcançado. O plano para aCOALESCE
versão está abaixo.Essa reescrita da consulta parece evitar o problema
O que dá plano
fonte
Outro exemplo
A pergunta
Não mostra nenhuma leitura
T2
.A busca de
T2
está sob uma passagem pelo predicado e o operador nunca é executado. MasSerá que mostram que
T2
é lido. Mesmo que nenhum valorT2
seja realmente necessário.É claro que isso não é realmente surpreendente, mas achei que vale a pena adicionar ao repositório de contra-exemplos, apenas porque levanta a questão do que significa curto-circuito em uma linguagem declarativa baseada em conjunto.
fonte
Eu só queria mencionar uma estratégia que você pode não ter considerado. Pode não ser uma correspondência aqui, mas às vezes é útil. Veja se esta modificação oferece um desempenho melhor:
Outra maneira de fazer isso pode ser isso (basicamente equivalente, mas permite acessar mais colunas da outra consulta, se necessário):
Basicamente, essa é uma técnica de união de tabelas "rígida", mas inclui a condição de quando todas as linhas devem ser JOINed. Na minha experiência, isso realmente ajudou os planos de execução às vezes.
fonte
Não, não seria. Seria executado apenas quandoc.FirstName
éNULL
.No entanto, você deve tentar você mesmo. Experimentar. Você disse que sua subconsulta é longa. Referência. Tire suas próprias conclusões sobre isso.A resposta do @Aaron na subconsulta em execução é mais completa.
No entanto, ainda acho que você deve refazer sua consulta e usar
LEFT JOIN
. Na maioria das vezes, as subconsultas podem ser removidas retrabalhando sua consulta para usarLEFT JOIN
s.O problema com o uso de subconsultas é que sua instrução geral será executada mais lentamente porque a subconsulta é executada para cada linha no conjunto de resultados da consulta principal.
fonte
O padrão atual diz que todas as cláusulas WHEN (assim como a cláusula ELSE) devem ser analisadas para determinar o tipo de dados da expressão como um todo. Eu realmente teria que tirar algumas das minhas anotações antigas para determinar como um erro é tratado. Mas, por pouco, o 1/0 usa números inteiros, então eu presumo que, embora seja um erro. É um erro com o tipo de dados inteiro. Quando você tem apenas nulos na lista de coalescência, é um pouco mais difícil determinar o tipo de dados, e esse é outro problema.
fonte