Quais são as tabelas com melhor desempenho, CTE ou temporárias?


Respostas:


62

Eu diria que eles são conceitos diferentes, mas não muito diferentes para dizer "giz e queijo".

  • Uma tabela temporária é boa para reutilizar ou para executar várias passagens de processamento em um conjunto de dados.

  • Um CTE pode ser usado para recompensar ou simplesmente para melhorar a legibilidade.
    E, como uma função com valor de exibição ou tabela embutida, também pode ser tratada como uma macro a ser expandida na consulta principal

  • Uma tabela temporária é outra tabela com algumas regras em torno do escopo

Eu armazenei procs onde eu uso ambos (e variáveis ​​de tabela também)


12
As tabelas temporárias também permitem índices e até estatísticas que às vezes são necessárias, enquanto uma CTE não.
CodeCowboyOrg 4/14

9
Penso que esta resposta não destaca suficientemente o fato de que as CTE podem levar a um desempenho terrível. Normalmente, refiro-me a esta resposta no dba.stackexchange. Sua pergunta aparece em segundo lugar no meu mecanismo de pesquisa, se eu estiver pesquisando, cte vs temporary tablesentão IMHO esta resposta precisa destacar as desvantagens da CTE. TL; DR da resposta vinculada: uma CTE nunca deve ser usada para desempenho. . Concordo com essa citação, pois experimentei as desvantagens da CTE.
TT.

2
@TT. Interessante. Acho que CTEs desempenho muito melhor
Squ1rr3lz

197

Depende.

Em primeiro lugar

O que é uma expressão de tabela comum?

Um CTE (não recursivo) é tratado de maneira muito semelhante a outras construções que também podem ser usadas como expressões de tabela embutida no SQL Server. Tabelas derivadas, exibições e funções com valor de tabela embutida. Observe que, embora a BOL diga que um CTE "pode ​​ser considerado um conjunto de resultados temporário", essa é uma descrição puramente lógica. Na maioria das vezes, não é materializado por si só.

O que é uma tabela temporária?

Esta é uma coleção de linhas armazenadas nas páginas de dados no tempdb. As páginas de dados podem residir parcial ou totalmente na memória. Além disso, a tabela temporária pode ser indexada e ter estatísticas de coluna.

Dados de teste

CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;

Exemplo 1

WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780

Plano 1

Observe no plano acima que não há menção à CTE1. Apenas acessa diretamente as tabelas base e é tratado da mesma forma que

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Reescrever materializando o CTE em uma tabela temporária intermediária aqui seria massivamente contraproducente.

Materializando a definição CTE de

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Isso envolveria a cópia de cerca de 8 GB de dados em uma tabela temporária, e ainda haverá a sobrecarga de seleção dela.

Exemplo 2

WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

O exemplo acima leva cerca de 4 minutos na minha máquina.

Somente 15 linhas dos 1.000.000 de valores gerados aleatoriamente correspondem ao predicado, mas a varredura cara da tabela acontece 16 vezes para localizá-los.

insira a descrição da imagem aqui

Este seria um bom candidato para materializar o resultado intermediário. A reescrita da tabela temporária equivalente levou 25 segundos.

INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

Com plano

A materialização intermediária de parte de uma consulta em uma tabela temporária às vezes pode ser útil, mesmo que seja avaliada apenas uma vez - quando permite que o restante da consulta seja recompilada, aproveitando as estatísticas do resultado materializado. Um exemplo dessa abordagem está no artigo SQL Cat, Quando dividir consultas complexas .

Em algumas circunstâncias, o SQL Server usará um spool para armazenar em cache um resultado intermediário, por exemplo, de um CTE e evitar a necessidade de reavaliar a subárvore. Isso é discutido no item Connect (migrado) Forneça uma dica para forçar a materialização intermediária de CTEs ou tabelas derivadas . No entanto, nenhuma estatística é criada sobre isso e, mesmo que o número de linhas em spool seja muito diferente do estimado, não é possível que o plano de execução em andamento se adapte dinamicamente em resposta (pelo menos nas versões atuais. Planos de consulta adaptáveis ​​podem se tornar possíveis em o futuro).


33
Essa é a única resposta que responde à pergunta real (que está perguntando qual é o melhor desempenho, não qual é a diferença ou qual é a sua favorita) e responde a essa pergunta corretamente: "Depende" é a resposta certa. É também a única resposta com dados de suporte a explicar, várias outras (com alto número de votos) fazem afirmações definidas de que uma é melhor que a outra sem referências ou provas ... Para ficar claro, todas essas respostas também estão erradas . Porque "Depende"
Arkaine55

2
É também uma resposta bem escrita e bem referenciada. Sério alto nível.
Dan Williams

50

O CTE tem seus usos - quando os dados no CTE são pequenos e há uma forte melhoria na legibilidade, como no caso das tabelas recursivas. No entanto, seu desempenho certamente não é melhor que as variáveis ​​de tabela e, quando se lida com tabelas muito grandes, as tabelas temporárias superam significativamente o CTE. Isso ocorre porque você não pode definir índices em um CTE e quando você possui uma grande quantidade de dados que requerem associação com outra tabela (o CTE é simplesmente como uma macro). Se você estiver juntando várias tabelas com milhões de linhas de registros em cada uma, o CTE terá um desempenho significativamente pior que as tabelas temporárias.


9
Eu já vi isso da minha própria experiência. Os CTEs apresentam desempenho significativamente mais lento.
Goku_da_master

7
As CTEs também apresentam desempenho mais lento porque os resultados não são armazenados em cache. Portanto, toda vez que você usa o CTE, ele executa novamente a consulta, o plano e tudo.
goku_da_master 25/01

1
E o mecanismo db pode optar por executar novamente a consulta não apenas em todas as referências, mas em todas as linhas da consulta do consumidor, como uma subconsulta correlacionada ... você deve sempre atentar para isso, se não for desejado.
Mike M

A tabela temporária é armazenada em tempdb no SQL Server, que é um disco, mas tem o benefício de ser indexado, e o otimizador SQL funciona bem em consultas selecionadas nesse caso. Não sabe em qual banco de dados ou área de disco o CTE está armazenado (quando excede o tamanho da memória e está na fila para paginação IO), mas nunca é otimizado com o grande volume de dados. Eu usei a opção de compilador (com recompilação) às vezes para torná-lo mais rápido
rmehra76 16/06

33

As tabelas temporárias estão sempre no disco - portanto, enquanto o seu CTE puder ser mantido na memória, provavelmente será mais rápido (como uma variável de tabela também).

Mas, novamente, se o carregamento de dados do seu CTE (ou variável da tabela temporária) ficar muito grande, ele também será armazenado em disco, para que não haja grande benefício.

Em geral, eu prefiro um CTE em vez de uma tabela temporária, uma vez que desapareceu depois que o usei. Não preciso pensar em soltá-lo explicitamente ou algo assim.

Portanto, nenhuma resposta clara no final, mas pessoalmente, eu preferiria o CTE em vez de tabelas temporárias.


2
No caso do SQLite e PostgreSQL, as tabelas temporárias são eliminadas automaticamente (geralmente no final de uma sessão). Eu não sei sobre outros DBMS embora.
Serrano

1
CTE é como uma visualização temporária. Os dados do AFAIK não são armazenados, portanto, nada é guardado na memória ou armazenado no disco. Nota importante, sempre que você usa o CTE, a consulta é executada novamente.
Rob

1
Pessoalmente, nunca vi um CTE funcionar melhor do que uma tabela Temp para obter velocidade. E bem depuração é muito mais fácil com a tabela temporária
Mark Monforti

7

Portanto, a consulta que fui designada para otimizar foi escrita com dois CTEs no SQL Server. Demorava 28 segundos.

Passei dois minutos convertendo-os em tabelas temporárias e a consulta levou 3 segundos

Adicionei um índice à tabela temporária no campo em que estava sendo associado e reduzi-o para 2 segundos

Três minutos de trabalho e agora rodam 12x mais rápido, removendo o CTE. Pessoalmente, não usarei CTEs, pois eles são mais difíceis de depurar também.

O mais louco é que os CTEs foram usados ​​apenas uma vez e ainda colocando um índice neles provou ser 50% mais rápido.


6

O CTE não ocupa espaço físico. É apenas um conjunto de resultados que podemos usar join.

As tabelas temporárias são temporárias. Podemos criar índices, restrições, como tabelas normais, para as quais precisamos definir todas as variáveis.

Escopo da tabela temporária apenas dentro da sessão. EX: Abra duas janelas de consulta SQL

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

Execute esta consulta na primeira janela e execute a consulta abaixo na segunda janela. Você pode encontrar a diferença.

select * from #temp

4
>> "é apenas um conjunto de resultados que podemos usar join." -> Isso não é exato. O CTE não é um "conjunto de resultados", mas um código embutido. O mecanismo de consulta do SQL Server analisa o código CTE como parte do texto da consulta e cria um plano de execução de acordo. A ideia de que CTE está em linha é a grande vantagem de usar CTE, uma vez que permite que o servidor para criar um "combinar plano de execução"
Ronen Ariely

4

Eu usei ambos, mas em procedimentos complexos e massivos sempre achei as tabelas temporárias melhores para trabalhar e mais metódicas. CTEs têm seus usos, mas geralmente com dados pequenos.

Por exemplo, eu criei sprocs que retornam com resultados de grandes cálculos em 15 segundos, mas convertem esse código para execução em um CTE e o viram executado em mais de 8 minutos para obter os mesmos resultados.


3

Tarde da festa, mas ...

O ambiente em que trabalho é altamente restrito, dando suporte a alguns produtos de fornecedores e fornecendo serviços de "valor agregado", como relatórios. Devido a limitações de política e contrato, normalmente não me é permitido o luxo de tabela / espaço de dados separados e / ou a capacidade de criar código permanente [fica um pouco melhor, dependendo do aplicativo].

IOW, normalmente não consigo desenvolver um procedimento armazenado ou UDFs ou tabelas temporárias, etc. Preciso fazer tudo através da MINHA interface do aplicativo (Crystal Reports - adicionar / vincular tabelas, definir cláusulas where de w / in CR, etc. ) Uma pequena graça salvadora é que o Crystal me permite usar COMMANDS (e também expressões SQL). Algumas coisas que não são eficientes através do recurso regular de tabelas de adição / vinculação podem ser feitas através da definição de um comando SQL. Eu uso CTEs com isso e obtive resultados muito bons "remotamente". Os CTEs também ajudam na manutenção de relatórios, não exigindo que o código seja desenvolvido, entregue a um DBA para compilar, criptografar, transferir, instalar e, em seguida, exigir testes em vários níveis. Eu posso fazer CTEs através da interface local.

O lado negativo de usar CTEs com CR é que cada relatório é separado. Cada CTE deve ser mantida para cada relatório. Onde eu posso fazer SPs e UDFs, posso desenvolver algo que pode ser usado por vários relatórios, exigindo apenas vinculação ao SP e passagem de parâmetros como se você estivesse trabalhando em uma tabela regular. O CR não é realmente bom em manipular parâmetros nos comandos SQL, portanto, esse aspecto do CR / CTE pode estar ausente. Nesses casos, geralmente tento definir o CTE para retornar dados suficientes (mas não TODOS os dados) e, em seguida, uso os recursos de seleção de registros no CR para dividir e dividir esses dados.

Então ... meu voto é para CTEs (até eu obter meu espaço de dados).


3

Um uso em que achei o desempenho excelente do CTE foi o local em que eu precisava ingressar em uma consulta relativamente complexa em algumas tabelas com alguns milhões de linhas cada.

Usei o CTE para selecionar primeiro o subconjunto com base nas colunas indexadas para primeiro cortar essas tabelas para alguns milhares de linhas relevantes cada uma e depois associá-las à minha consulta principal. Isso reduziu exponencialmente o tempo de execução da minha consulta.

Embora os resultados para o CTE não sejam armazenados em cache e as variáveis ​​de tabela possam ter sido uma escolha melhor, eu realmente só queria experimentá-las e encontrar o ajuste no cenário acima.


Além disso, eu acho que desde que eu só usar o CTE na junção Eu só realmente executar o CTE uma vez na minha consulta para cache os resultados não era um grande problema a este respeito
purchas

1

Essa é uma pergunta realmente aberta, e tudo depende de como está sendo usada e do tipo de tabela temporária (variável de tabela ou tabela tradicional).

Uma tabela temporária tradicional armazena os dados no banco de dados temp, o que diminui a velocidade das tabelas temporárias; no entanto, variáveis ​​de tabela não.


1

Acabei de testar isso - tanto o CTE quanto o não CTE (onde a consulta foi digitada para cada instância da união) levaram ~ 31 segundos. O CTE tornou o código muito mais legível - reduziu de 241 para 130 linhas, o que é muito bom. A tabela temporária, por outro lado, reduziu para 132 linhas e levou CINCO SEGUNDOS para executar. Não é brincadeira. todo esse teste foi armazenado em cache - as consultas foram todas executadas várias vezes antes.


1

Pela minha experiência no SQL Server, encontrei um dos cenários em que o CTE superou a tabela Temp

Eu precisava usar um DataSet (~ 100000) de uma consulta complexa apenas uma vez no meu procedimento armazenado.

  • A tabela Temp estava causando uma sobrecarga no SQL, onde meu Procedimento estava executando lentamente (como Tabelas Temp são tabelas materializadas reais que existem no tempdb e Persistem durante toda a vida útil do meu procedimento atual)

  • Por outro lado, com o CTE, o CTE persiste apenas até que a consulta a seguir seja executada. Portanto, o CTE é uma estrutura útil na memória com escopo limitado. CTEs não usam tempdb por padrão.

Este é um cenário em que os CTEs podem realmente ajudar a simplificar o seu código e superar a tabela temporária. Eu tinha usado 2 CTEs, algo como

WITH CTE1(ID, Name, Display) 
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO

1
Sua resposta parece ser muito genérica ... Como você mede essa "tabela Temp superada pela CTE"? Você tem algumas medidas de tempo? Na minha opinião, você deve editar sua resposta e adicionar mais detalhes.
Il Vic

Sim, tenho medições de tempo e plano de execução para apoiar minha declaração.
Amardeep Kohli

Não é possível adicionar o img para plano de execução por causa da limitada detalhes de atualização privileges.Will uma vez que seja resolvido
Amardeep Kohli
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.