Quais são de melhor desempenho CTE
ou Temporary Tables
?
Quais são de melhor desempenho CTE
ou Temporary Tables
?
Respostas:
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)
cte vs temporary tables
entã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.
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
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.
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
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).
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.
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.
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.
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
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.
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).
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.
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.
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.
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