Estou usando uma COALESCE
função T-SQL em que o primeiro argumento não será nulo em cerca de 95% das vezes em que for 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 executaria a subconsulta?
Eu sei que 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).
Não . Aqui está um teste simples:
Se a segunda condição for avaliada, uma exceção será lançada para divisão por zero.
De acordo com a documentação do MSDN , isso está relacionado a como
COALESCE
é visto pelo intérprete - é apenas uma maneira fácil de escrever umaCASE
declaração.CASE
é bem conhecido por ser uma das únicas funções no SQL Server que (principalmente) entram em curto-circuito de forma confiável.Existem algumas exceções ao comparar variáveis e agregações escalares, conforme mostrado por Aaron Bertrand em outra resposta aqui (e isso se aplicaria a
CASE
eCOALESCE
):irá gerar uma divisão por erro zero.
Isso deve ser considerado um bug e, como regra
COALESCE
, será analisado da esquerda para a direita.Que tal este - conforme relatado a mim por Itzik Ben-Gan, que foi informado por Jaime Lafargue ?
Resultado:
Existem soluções alternativas 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 "propositalmente". Paul White subseqüentemente arquivou este item 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 as agregações podem alterar a ordem de avaliação de umaCASE
expressão. Recentemente, escrevi mais sobre isso aqui .EDIT apenas um adendo, embora eu concorde que esses são casos extremos, que na maioria das vezes você pode confiar na avaliação e curto-circuito da esquerda para a direita, e que esses são bugs que contradizem a documentação e provavelmente serão corrigidos eventualmente ( isso não é definitivo - veja a conversa de acompanhamento na postagem do blog de Bart Duncan para ver o porquê), tenho que discordar quando as pessoas dizem que algo é sempre verdade, mesmo que haja um único caso extremo que o refute. Se Itzik e outros podem encontrar bugs solitários como este, pelo menos existe a possibilidade de que existam outros bugs também. E como não sabemos o restante da consulta do OP, não podemos dizer com certeza que ele vai contar com esse curto-circuito, mas acabará sendo mordido por ele. Então, para mim, a resposta mais segura é:
Embora você geralmente possa
CASE
avaliar a esquerda para a direita e o curto-circuito, conforme descrito na documentação, não é correto dizer que você sempre pode fazer isso. Há dois casos demonstrados nesta página em que isso não é verdade e nenhum bug foi corrigido em qualquer versão disponível publicamente do SQL Server.EDIT aqui é outro caso (preciso parar de fazer isso) em que uma
CASE
expressão não é avaliada na ordem que você esperaria, mesmo que nenhuma agregação esteja envolvida.A documentação deixa razoavelmente claro que a intenção é causar
CASE
um curto-circuito. Como Aaron menciona , houve vários casos relatados em que isso nem sempre é verdade. Até agora, a maioria deles foi reconhecida como bugs e corrigida.Existem outros problemas com
CASE
(e, portanto,COALESCE
) onde as funções de efeito colateral ou subconsultas são usadas. Considerar:O
COALESCE
formulário geralmente retorna nulo, conforme descrito em um relatório de bug de Hugo Kornelis.Os problemas demonstrados com as transformações do otimizador e o rastreamento de expressão comum significam que é impossível garantir que
CASE
haverá um curto-circuito em todas as circunstâncias.Eu acho que você pode estar razoavelmente confiante de que
CASE
haverá um curto-circuito em geral (particularmente se uma pessoa razoavelmente qualificada inspecionar o plano de execução e esse plano de execução for 'aplicado' com um guia de plano ou dicas), mas se você precisar de uma garantia absoluta, você tem que escrever SQL que não inclua a expressão.Eu me deparei com outro caso em
CASE
queCOALESCE
não curto-circuito. O TVF a seguir gerará uma violação de PK se for passado1
como parâmetro.Se chamado da seguinte forma
Ou como
Ambos dão o resultado
mostrando que
SELECT
(ou pelo menos a população da variável da tabela) ainda é realizada e gera um erro, mesmo que esse ramo da instrução nunca deva ser alcançado. O plano para aCOALESCE
versão está abaixo.Esta reescrita da consulta parece evitar o problema
que dá plano
Outro exemplo
A pergunta
Não mostra nenhuma leitura
T2
.A busca de
T2
está sob um predicado de passagem e o operador nunca é executado. MasMostra que
T2
é lido. Mesmo que nenhum valor fromT2
seja realmente necessário.É claro que isso não é realmente surpreendente, mas achei que valia a pena adicionar ao repositório de contra-exemplos apenas porque levanta a questão do que significa um curto-circuito em uma linguagem declarativa baseada em conjunto.
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 lhe dá algum desempenho melhor:
Outra maneira de fazer isso pode ser esta (basicamente equivalente, mas permite acessar mais colunas da outra consulta, se necessário):
Basicamente, esta é uma técnica de junção "forte" de tabelas, mas incluindo a condição de quando qualquer linha deve ser unida. Na minha experiência, isso realmente ajudou os planos de execução às vezes.
O padrão atual diz que todas as cláusulas WHEN (bem como a cláusula ELSE) devem ser analisadas para determinar o tipo de dados da expressão como um todo. Eu realmente teria que pegar algumas das minhas anotações antigas para determinar como um erro é tratado. Mas, de imediato, 1/0 usa números inteiros, então eu diria 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 complicado determinar o tipo de dados, e esse é outro problema.
Não, não seria. Só seria executado quandoc.FirstName
forNULL
.No entanto, você deve tentar por si mesmo. Experimentar. Você disse que sua subconsulta é longa. Referência. Tire suas próprias conclusões sobre isso.A resposta @Aaron na subconsulta sendo executada é mais completa.
No entanto, ainda acho que você deve retrabalhar 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.