Qual é a diferença entre uma CTE e uma tabela temporária?

174

Qual é a diferença entre uma Expressão de tabela comum (CTE) e uma tabela temporária? E quando devo usar um sobre o outro?

CTE

WITH cte (Column1, Column2, Column3)
AS
(
    SELECT Column1, Column2, Column3
    FROM SomeTable
)

SELECT * FROM cte

Tabela Temp

SELECT Column1, Column2, Column3
INTO #tmpTable
FROM SomeTable

SELECT * FROM #tmpTable
Rachel
fonte

Respostas:

200

Isso é bem amplo, mas eu darei uma resposta o mais geral possível.

CTEs ...

  • Não são indexáveis ​​(mas podem usar índices existentes em objetos referenciados)
  • Não pode ter restrições
  • São essencialmente descartáveis VIEWs
  • Persistir apenas até a próxima consulta ser executada
  • Pode ser recursivo
  • Não possui estatísticas dedicadas (confie nas estatísticas dos objetos subjacentes)

#Temp Tables ...

  • São tabelas materializadas reais que existem no tempdb
  • Pode ser indexado
  • Pode ter restrições
  • Persistir pela vida da CONEXÃO atual
  • Pode ser referenciado por outras consultas ou subprocedimentos
  • Ter estatísticas dedicadas geradas pelo mecanismo

Quanto a quando usar cada um, eles têm casos de uso muito diferentes. Se você tiver um conjunto de resultados muito grande ou precisar consultá-lo mais de uma vez, coloque-o em uma #temptabela. Se ele precisa ser recursivo, é descartável ou é apenas para simplificar algo logicamente, a CTEé o preferido.

Além disso, um nuncaCTE deve ser usado para desempenho . Você quase nunca acelera as coisas usando um CTE, porque, novamente, é apenas uma visão descartável. Você pode fazer algumas coisas legais com eles, mas acelerar uma consulta não é realmente uma delas.

JNK
fonte
acelerar um grande MERGE usando CTE é uma coisa
AgentFire
1
Acelerar muitas consultas usando CTEs também é importante, porque com as CTEs você pode adicionar seu próprio conhecimento comercial para superar o otimizador de consultas. Por exemplo, você pode selecionar a parte 1 do seu CTE nas tabelas em que você sabe que as linhas resultantes serão muito pequenas. Dentro da mesma consulta, você pode associar esse pequeno conjunto de resultados a um conjunto maior de resultados e ignorar completamente os problemas causados ​​por estatísticas obsoletas etc. Para fazer isso, é necessário adicionar dicas de consulta para forçar o pedido. Funciona, melhora o desempenho.
Dave Hilditch
"nunca ser usado para desempenho" é uma afirmação ampla e um tanto subjetiva, embora eu entenda seu ponto de vista. Embora, além dos outros comentários, outro ganho potencial de desempenho ao usar um CTE possa ocorrer ao alternar para um CTE recursivo de outra forma de recursão, como chamadas de procedimento recursivo ou um cursor.
JD
29

EDITAR:

Por favor, veja os comentários de Martin abaixo:

O CTE não é materializado como uma tabela na memória. É apenas uma maneira de encapsular uma definição de consulta. No caso do PO, ele será incorporado e o mesmo que apenas fazer SELECT Column1, Column2, Column3 FROM SomeTable. Na maioria das vezes eles não são materializados antecipadamente, e é por isso que isso não retorna linhas WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.X, verifique também os planos de execução. Embora às vezes seja possível hackear o plano para obter um carretel. Há um item de conexão solicitando uma dica para isso. - Martin Smith Feb 15 '12 às 17:08


Resposta original

CTE

Leia mais no MSDN

Um CTE cria a tabela que está sendo usada na memória, mas é válida apenas para a consulta específica a seguir. Ao usar a recursão, isso pode ser uma estrutura eficaz.

Você também pode considerar o uso de uma variável de tabela. Isso é usado quando uma tabela temporária é usada e pode ser usada várias vezes sem precisar ser materializado novamente para cada junção. Além disso, se você precisar persistir alguns registros agora, adicione mais alguns registros após a próxima seleção, adicione mais alguns registros após outra operação e, em seguida, retorne apenas alguns poucos registros, pois isso pode ser uma estrutura útil, pois não precisa ser descartado após a execução. Principalmente apenas açúcar sintático. No entanto, se você mantiver a contagem de linhas baixa, ela nunca se materializa em disco. Consulte Qual é a diferença entre uma tabela temporária e uma variável de tabela no SQL Server? para mais detalhes.

Tabela Temp

Leia mais no MSDN - role para baixo cerca de 40% do caminho

Uma tabela temporária é literalmente uma tabela criada em disco, apenas em um banco de dados específico que todos sabem que pode ser excluído. É de responsabilidade de um bom desenvolvedor destruir essas tabelas quando elas não são mais necessárias, mas um DBA também pode limpá-las.

As tabelas temporárias são fornecidas em duas variedades: local e global. Em termos de MS Sql Server, você usa uma #tableNamedesignação para local e ##tableNamedesignação para global (observe o uso de um único ou duplo # como a característica de identificação).

Observe que, com tabelas temporárias, ao contrário de variáveis ​​de tabela ou CTE, é possível aplicar índices e similares, pois são legitimamente tabelas no sentido normal da palavra.


Geralmente eu usaria tabelas temporárias para consultas maiores ou maiores, e CTEs ou variáveis ​​de tabela se já tivesse um pequeno conjunto de dados e quisesse rapidamente criar um pouco de código para algo pequeno. A experiência e os conselhos de outras pessoas indicam que você deve usar CTEs nas quais um pequeno número de linhas é retornado. Se você tiver um número grande, provavelmente se beneficiaria da capacidade de indexar na tabela temporária.

jcolebrand
fonte
11
O CTE não é materializado como uma tabela na memória. É apenas uma maneira de encapsular uma definição de consulta. No caso do OP, ele será incorporado e o mesmo que apenas fazerSELECT Column1, Column2, Column3 FROM SomeTable
Martin Smith
4
Na maioria das vezes eles não são materializados antecipadamente, e é por isso que isso não retorna linhas WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.X, verifique também os planos de execução. Embora às vezes seja possível hackear o plano para obter um carretel. Há um item de conexão solicitando uma dica para isso.
Martin Smith
16

A resposta aceita aqui diz que "uma CTE nunca deve ser usada para desempenho" - mas isso pode enganar. No contexto de CTEs versus tabelas temporárias, acabei de remover uma faixa de lixo de um conjunto de procs armazenados, porque alguns idiotas devem ter pensado que havia pouca ou nenhuma sobrecarga no uso de tabelas temporárias. Enfiei o lote nas CTEs, exceto aquelas que seriam legitimamente reutilizadas durante todo o processo. Ganhei cerca de 20% de desempenho em todas as métricas. Comecei a remover todos os cursores que estavam tentando implementar o processamento recursivo. Foi aqui que vi o maior ganho. Acabei reduzindo o tempo de resposta por um fator de dez.

CTEs e tabelas temporárias têm casos de uso muito diferentes. Eu só quero enfatizar que, embora não seja uma panacéia, a compreensão e o uso correto de CTEs podem levar a algumas melhorias realmente estelares na qualidade / manutenção e velocidade do código. Desde que eu os identifiquei, vejo tabelas temporárias e cursores como os grandes males do processamento SQL. Eu posso me dar bem com variáveis ​​de tabela e CTEs para quase tudo agora. Meu código é mais limpo e rápido.

Mel Padden
fonte
Agora, sejamos justos - os cursores são o grande mal; as tabelas temporárias são, na pior das hipóteses, um mal menor . :-) É realmente injusto colocá-los no mesmo nível, como você se viu.
precisa saber é o seguinte
@RDFozz certo, o inferno tem 9 círculos, como todos sabemos . Vamos colocar tabelas temporárias no 2º e cursores no ... 7º? ;)
ypercubeᵀᴹ
1
Você sabe o que o 'grande mal' está na programação? Quando as pessoas dizem que uma técnica específica é má. Há um lugar para cursores. Eles podem superar outras técnicas em determinados cenários. Não há mal aqui - você precisa aprender a usar a ferramenta certa para o trabalho. Avalie o que você está fazendo e não acredite no hype de que CTE, Temp Tables ou Cursors são maus. Medida - porque a verdade depende do cenário.
Dave Hilditch
O @DaveHilditch é um comentário justo, mas também é justo afirmar que, em muitas situações, os cursores não são a solução certa, por isso é uma generalização viável tê-los, bem como quase um último recurso.
Mel Padden
1
Na minha experiência, um CURSOR não é ruim por si só. Os CURSORS são geralmente "erroneamente" usados ​​pelos desenvolvedores porque, na maioria das linguagens de programação, você precisa pensar iterativamente, em oposição ao SQL, onde você geralmente pensa em lotes. Sei que esse é um erro comum no meu local de trabalho, onde os Devs simplesmente não conseguem "ver" uma saída para um problema que não seja um CURSOR, e por isso um bom DBA é útil para ensiná-los e corrigi-los. O @DaveHilditch está totalmente certo: a ferramenta certa para o trabalho certo é o suficiente.
Philippe
14

Um CTE pode ser chamado repetidamente em uma consulta e é avaliado toda vez que é referenciado - esse processo pode ser recursivo. Se for apenas referido uma vez, ele se comportará como uma subconsulta, embora os CTEs possam ser parametrizados.

Uma tabela temporária é mantida fisicamente e pode ser indexada. Na prática, o otimizador de consulta também pode persistir nos resultados intermediários de junção ou subconsulta nos bastidores, como em operações de spool, portanto, não é estritamente verdade que os resultados dos CTEs nunca sejam persistidos no disco.

As variáveis ​​de tabela IIRC (por outro lado) são sempre estruturas na memória.

ConcernedOfTunbridgeWells
fonte
4
CTEs podem ser parametrizados? Quão? Além disso, as variáveis ​​de tabela nem sempre são estruturas na memória. Veja a excelente resposta de Martin para uma pergunta relacionada.
Paul White
11

A tabela Temp é um objeto real no tempdb, mas cte é apenas um tipo de invólucro em torno de consultas complexas para simplificar a sintaxe da recursão da organização em uma única etapa.

Oleg Dok
fonte
8

O principal motivo para usar CTEs é acessar as Funções da Janela, como row_number()várias outras.

Isso significa que você pode fazer coisas como obter a primeira ou a última linha por grupo MUITO MUITO rápido e eficiente - com mais eficiência do que outros meios na maioria dos casos práticos .

with reallyfastcte as (
select *, 
row_number() over (partition by groupingcolumn order by sortingcolumn) as rownum
from sometable
)
select *
from reallyfastcte
where rownum = 1;

Você pode executar uma consulta semelhante à anterior usando uma subconsulta correlacionada ou usando uma subconsulta, mas o CTE será mais rápido em quase todos os cenários.

Além disso, os CTEs podem realmente ajudar a simplificar seu código. Isso pode levar a ganhos de desempenho, porque você entende mais a consulta e pode introduzir mais lógica de negócios para ajudar o otimizador a ser mais seletivo.

Além disso, os CTEs podem melhorar o desempenho se você entender sua lógica de negócios e souber quais partes da consulta devem ser executadas primeiro - normalmente, coloque primeiro as consultas mais seletivas que levam a conjuntos de resultados que podem usar um índice na próxima associação e adicionar a option(force order)consulta dica

Por fim, os CTEs não usam o tempdb por padrão, então você reduz a contenção nesse gargalo através do uso deles.

Tabelas temporárias devem ser usadas se você precisar consultar os dados várias vezes ou, alternativamente, se você medir suas consultas e descobrir isso inserindo em uma tabela temporária e adicionando um índice para melhorar seu desempenho.

Dave Hilditch
fonte
todos os bons pontos ... +1
Mel Padden
6

Parece haver um pouco de negatividade aqui em relação às CTEs.

Meu entendimento de um CTE é que é basicamente um tipo de visão ad-hoc. O SQL é uma linguagem declarativa e uma baseada em conjunto. Os CTEs são uma ótima maneira de declarar um conjunto! Não ser capaz de indexar uma CTE é realmente uma coisa boa, porque você não precisa! É realmente um tipo de açúcar sintático para facilitar a leitura / gravação da consulta. Qualquer otimizador decente elaborará o melhor plano de acesso usando índices nas tabelas subjacentes. Isso significa que você pode efetivamente acelerar sua consulta CTE seguindo os conselhos do índice nas tabelas subjacentes.

Além disso, apenas porque você definiu um conjunto como um CTE, isso não significa que todas as linhas no conjunto devem ser processadas. Dependendo da consulta, o otimizador pode processar linhas "apenas o suficiente" para satisfazer a consulta. Talvez você só precise dos 20 primeiros para sua tela. Se você construiu uma tabela temporária, realmente precisa ler / gravar todas essas linhas!

Com base nisso, eu diria que os CTEs são um ótimo recurso do SQL e podem ser usados ​​em qualquer lugar que facilite a leitura da consulta. Eu pensaria apenas em uma tabela temporária para um processo em lote que realmente precisaria processar todos os registros. Mesmo assim, o afaik não é realmente recomendado, porque em uma tabela temporária é muito mais difícil para o banco de dados ajudá-lo com o cache e os índices. Talvez seja melhor ter uma tabela permanente com um campo PK exclusivo para sua transação.

Eu tenho que admitir que minha experiência é principalmente com o DB2, então estou assumindo que o CTE funcione de maneira semelhante nos dois produtos. Ficarei feliz em corrigi-lo se os CTEs forem de alguma forma inferiores no SQL Server. ;)

Ben Thurley
fonte