Tome o seguinte exemplo:
SELECT <CalculationA> As ColA,
<CalculationB> As ColB,
<CalculationA> + <CalculationB> As ColC
FROM TableA
CalculationA e CalculationB, cada um, seriam calculados duas vezes?
Ou o otimizador seria inteligente o suficiente para calculá-los uma vez e usar o resultado duas vezes?
Eu gostaria de realizar um teste para ver o resultado por mim mesmo, porém, não tenho certeza de como eu poderia verificar algo assim.
Minha suposição é que ele realizaria o cálculo duas vezes.
Nesse caso, dependendo dos cálculos envolvidos, seria melhor usar uma tabela derivada ou uma visualização aninhada? Considere o seguinte:
SELECT TableB.ColA,
TableB.ColB,
TableB.ColA + TableB.ColB AS ColC,
FROM(
SELECT <CalculationA> As ColA,
<CalculationB> As ColB
FROM TableA
) As TableB
Nesse caso, eu esperaria que os cálculos fossem realizados apenas uma vez?
Por favor, alguém pode confirmar ou refutar minhas suposições? Ou me instruir sobre como testar algo assim para mim?
Obrigado.
A maioria das informações que você precisará estará no plano de execução (e no XML do plano).
Faça esta consulta:
O plano de execução (aberto com sentryone plan explorer ) mostra por quais etapas ele passou:
Com a agregação de fluxo agregando os valores para EXPR1005 e EXPR1006
Se quisermos saber o que são, podemos obter as informações exatas sobre essas expressões do XML do plano de consulta:
Com a primeira computação escalar de computação
ColA & ColB
:E o último escalar de computação sendo uma simples adição:
Isso é lê-lo à medida que os dados fluem, em teoria, você deve lê-lo da esquerda para a direita se estiver passando pela execução lógica.
Nesse caso,
EXPR1004
está chamando as outras expressões,EXPR1002
&EXPR1003
. Por sua vez, estes estão chamandoEXPR1005
&EXPR1006
.Testes anteriores mostram que neste caso
ColC
é simplificado como uma adição dos cálculos que são definidos comoColA
&ColB
.Como resultado,
ColA
&ColB
são calculados apenas uma vez.Agrupando por 200 valores distintos
Se estivermos agrupando por 200 valores distintos (val3) o mesmo é mostrado:
Agregando até esses 200 valores distintos em
val3
realizando as somas em val & val2 e, em seguida, somando-as para ColC:
Mesmo se estivermos agrupando todos, exceto um valor não exclusivo, a mesma adição deve ser vista para o escalar de computação.
Adicionando uma função a ColA e ColB
Mesmo se mudarmos a consulta para isso:
As agregações ainda não serão calculadas duas vezes, estamos simplesmente adicionando a
ABS()
função ao conjunto de resultados da agregação, que é uma linha:É claro que executar
SUM(ABS(ColA)
&SUM(ABS(ColB))
fará com que o otimizador não possa usar a mesma expressão para calcularColC
.Se você quiser se aprofundar quando isso acontecer, eu o levaria ao Query Optimizer Deep Dive - Parte 1 (até a Parte 4) de Paul White.
Outra maneira de se aprofundar nas fases de execução da consulta é adicionando estas dicas:
Isso irá expor a árvore de entrada conforme criada pelo otimizador.
A soma dos dois valores calculados anteriores para obter
ColC
é então traduzida para:Esta informação já está presente na Árvore de Entrada , mesmo antes da fase de simplificação, mostrando que o otimizador sabe imediatamente que não precisa realizar o mesmo cálculo duas vezes.
Se a primeira parte do cálculo for um cálculo real (
Col1 + Col2
) e não uma função, os cálculos individuais serão executados para cada etapa de "cálculo".Se substituirmos
<CalculationA>
sua declaração por um cálculo válido usandoColA
eColB
de uma tabela e repetirmos isso para cada<CalculationB>,...
etapa subsequente, a tarefa real de calcular o resultado será executada para cada etapa individualmente.Para reproduzir minha instrução, cole os seguintes trechos de código no SQL Server Management Studio e execute. Certifique-se de ter ativado a opção Incluir Plano de Execução Real .
Ele cria um banco de dados, uma tabela, preenche a tabela e realiza um cálculo que produz um plano de execução.
A consulta será executada e produzirá um plano de execução gráfico semelhante ao seguinte:
Como na resposta de Randi, vamos nos concentrar no operador Compute Scalar .
Se você clicar no Plano de Execução de Consulta no SSMS e clicar com o botão direito do mouse para mostrar o plano real:
.. você encontrará o seguinte XML (com foco na parte Compute Scalar ):
Assim, cada cálculo individual é executado repetidamente no caso de os valores serem recuperados de uma tabela real. O seguinte fragmento XML é do resumo acima:
Há cinco
<Arithmetic Operation="ADD">
etapas no plano de execução.Respondendo sua pergunta
Sim, se os cálculos forem somas reais de colunas conforme o exemplo. O último cálculo seria a soma de
CalculationA + CalculationB
.Depende do que você está calculando. - Neste exemplo: sim. - Na resposta de Randi: não.
Você está certo para certos cálculos.
Correto.
Depois de terminar, você pode descartar seu banco de dados novamente:
Como já existem boas respostas para a pergunta, vou me concentrar no aspecto DRY (DRY = não se repita).
Eu me acostumei a usar
CROSS APPLY
, se eu tiver que fazer os mesmos cálculos na mesma consulta várias vezes (não esqueçaGROUP BY / WHERE / ORDER BY
, onde os mesmos cálculos tendem a ocorrer repetidamente).Quando um cálculo depende de outro, não há motivo para não usar várias
CROSS APPLY
chamadas para calcular os resultados provisórios (o mesmo, quando você precisa usar o resultado final noWHERE
/ORDER BY
)O ponto principal para fazer isso é que você tem que modificar/corrigir apenas uma linha de código quando encontrar um bug ou tiver que alterar algo em vez de várias ocorrências e não correr o risco de ter várias (um pouco diferente, já que você esqueceu de alterar uma ) do mesmo cálculo.
PS: em relação à legibilidade
CROSS APPLY
, geralmente vencerá contra várias sub-seleções aninhadas (ou CTEs), principalmente quando os cálculos usam colunas de tabelas de origem diferentes ou você tem resultados provisórios.Se ambas as Expressões forem determinísticas, o otimizador reutilizará os resultados calculados anteriormente e simplesmente os adicionará. Se qualquer expressão for não determinística, essa expressão DEVE ser recalculada.