Existe uma diferença de desempenho entre CTE, subconsulta, tabela temporária ou variável de tabela?


222

Nesta excelente questão SO , as diferenças entre CTEesub-queries foram discutidas.

Gostaria de perguntar especificamente:

Em que circunstância cada um dos seguintes itens é mais eficiente / mais rápido?

  • CTE
  • Subconsulta
  • Tabela Temporária
  • Variável de tabela

Tradicionalmente, eu usei muito temp tablesno desenvolvimento stored procedures- pois eles parecem mais legíveis do que muitas subconsultas entrelaçadas.

Non-recursive CTEs encapsulam conjuntos de dados muito bem e são muito legíveis, mas existem circunstâncias específicas em que se pode dizer que sempre terão um desempenho melhor? ou é um caso de ter que sempre mexer nas diferentes opções para encontrar a solução mais eficiente?


EDITAR

Recentemente me disseram que, em termos de eficiência, as tabelas temporárias são uma boa primeira opção, pois possuem um histograma associado, ou seja, estatísticas.


4
Resposta geral: depende. E depende de alguns fatores, qualquer afirmação geral provavelmente é falsa - em algumas situações. Basicamente: você precisa testar e medir - veja qual funciona melhor para você!
marc_s

@marc_s - ok; talvez essa questão deva ser encerrada por ser subjetiva? Lembre-se de que muitas perguntas sobre SQL no SO podem ser consideradas subjetivas.
whytheq

1
Pode ser fechado por ser muito amplo - e eu concordo com você - muitas coisas e tópicos no SQL realmente obterão uma resposta de que depende . Às vezes, é possível listar dois ou três critérios para tomar uma decisão, mas com a sua pergunta aqui, é quase impossível dar bons conselhos - isso depende muito - da estrutura de sua tabela, dos dados nessas tabelas, das consultas que você está usando, sua estratégia de indexação e muito mais ....
marc_s 23/06

@marc_s seria bom tentar manter - algum conselho sobre possíveis edições no OP para tentar torná-lo mais específico e restrito?
whytheq

Observe que esta pergunta é específica para o SQL Server. Para outros bancos de dados como o Postgres, uma CTE é frequentemente muito mais lento do que subconsultas equivalentes (ver http://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/ )
Jay

Respostas:


243

SQL é uma linguagem declarativa, não uma linguagem processual. Ou seja, você constrói uma instrução SQL para descrever os resultados que deseja. Você não está dizendo ao mecanismo SQL como fazer o trabalho.

Como regra geral, é uma boa ideia deixar o mecanismo e o otimizador de SQL encontrar o melhor plano de consulta. Há muitos anos-pessoa dedicados ao desenvolvimento de um mecanismo SQL, portanto, deixe os engenheiros fazerem o que sabem fazer.

Obviamente, há situações em que o plano de consulta não é ideal. Então você deseja usar dicas de consulta, reestruturar a consulta, atualizar estatísticas, usar tabelas temporárias, adicionar índices e assim por diante para obter melhor desempenho.

Quanto à sua pergunta. O desempenho de CTEs e subconsultas deve, em teoria, ser o mesmo, pois ambos fornecem as mesmas informações para o otimizador de consultas. Uma diferença é que uma CTE usada mais de uma vez pode ser facilmente identificada e calculada uma vez. Os resultados podem ser armazenados e lidos várias vezes. Infelizmente, o SQL Server não parece tirar proveito desse método básico de otimização (você pode chamar essa eliminação de subconsulta comum).

As tabelas temporárias são uma questão diferente, porque você fornece mais orientações sobre como a consulta deve ser executada. Uma grande diferença é que o otimizador pode usar estatísticas da tabela temporária para estabelecer seu plano de consulta. Isso pode resultar em ganhos de desempenho. Além disso, se você tiver uma CTE (subconsulta) complicada usada mais de uma vez, armazená-la em uma tabela temporária geralmente oferecerá um aumento no desempenho. A consulta é executada apenas uma vez.

A resposta para sua pergunta é que você precisa brincar para obter o desempenho esperado, principalmente para consultas complexas que são executadas regularmente. Em um mundo ideal, o otimizador de consultas encontraria o caminho de execução perfeito. Embora isso aconteça com frequência, você poderá encontrar uma maneira de obter melhor desempenho.


11
Alguns Microsoft Research sobre possíveis melhorias futuras nesta área é na publicação "exploração eficiente de Sub-expressões similares para consulta Processing” Disponível a partir daqui
Martin Smith

3
Dado que esse documento foi apresentado em 2007, alguma idéia de se eles o incorporaram no SQL Server 2012?
Gordon Linoff

3
Uma ótima resposta! Apenas para enfatizar: SQL é uma linguagem declarativa e não controlamos COMO os dados são extraídos. Portanto, o desempenho / velocidade varia de consulta para consulta.
Simcha Khabinsky 31/03

2
@RGS. . . Os índices em tabelas temporárias definitivamente melhoram as consultas que podem tirar proveito desses índices - como nos índices de uma tabela permanente. Mas, se você materializar uma subconsulta como uma tabela temporária, poderá perder a vantagem dos índices nas tabelas originais.
Gordon Linoff

2
@RGS. . .Quando um mecanismo de banco de dados materializa uma subconsulta / CTE durante a execução de uma consulta complexa, ele não adiciona índices à materialização. Você pode fazer isso manualmente usando tabelas temporárias.
Gordon Linoff

77

Não existe regra. Acho as CTEs mais legíveis e as uso a menos que que apresentem algum problema de desempenho. Nesse caso, investigo o problema real em vez de supor que o CTE é o problema e tento reescrevê-lo usando uma abordagem diferente. Geralmente, há mais do que a maneira que escolhi declarar declaradamente minhas intenções com a consulta.

Certamente, há casos em que você pode desvendar CTEs ou remover subconsultas e substituí-las por uma tabela #temp e reduzir a duração. Isso pode ser devido a várias coisas, como estatísticas obsoletas, incapacidade de obter estatísticas precisas (por exemplo, ingressar em uma função com valor de tabela), paralelismo ou até incapacidade de gerar um plano ideal devido à complexidade da consulta ( nesse caso, quebrá-lo pode dar ao otimizador uma chance de lutar). Mas também existem casos em que a E / S envolvida na criação de uma tabela #temp pode superar os outros aspectos de desempenho que podem tornar um plano específico moldar usando um CTE menos atraente.

Honestamente, existem muitas variáveis ​​para fornecer uma resposta "correta" à sua pergunta. Não há uma maneira previsível de saber quando uma consulta pode se inclinar a favor de uma abordagem ou de outra - apenas saiba que, em teoria, a mesma semântica para uma CTE ou uma única subconsulta deve executar exatamente a mesma. Acho que sua pergunta seria mais valiosa se você apresentar alguns casos em que isso não é verdade - pode ser que você tenha descoberto uma limitação no otimizador (ou uma conhecida) ou pode ser que suas consultas não sejam semanticamente equivalentes ou aquele contém um elemento que impede a otimização.

Por isso, sugiro que você escreva a consulta da maneira que lhe parecer mais natural e só se desvie quando descobrir um problema real de desempenho que o otimizador está tendo. Pessoalmente, eu os classifico como CTE e, em seguida, subconsulta, com a #temp table sendo o último recurso.


4
+1 acabou sendo uma questão bastante subjetiva; Espero que não seja fechado por ser muito vago, pois as respostas até agora são informativas. Sei que :-) você não gosta quando as perguntas mudam, mas você tem alguma sugestão para restringir a pergunta no OP?
whytheq

2
Acho que essa pergunta está correta, você notará que ainda não há um único voto para fechar, mas se as respostas começarem a rolar descontroladamente, provavelmente será encerrada. Como sugeri na minha resposta, se você tem um caso específico em que vê uma grande diferença entre uma CTE e uma subconsulta, inicie uma nova pergunta com as consultas e planos de execução reais (e pode ser mais adequado para o dba.se ) . Apenas perceba que a resposta para ajudar nessa consulta pode não ser a mesma para uma consulta diferente com o mesmo cenário.
Aaron Bertrand

Logo abaixo da sua pergunta, existem links link / edit / close / flag- se houver votos para encerrá-la, você verá close (n)onde nrepresenta o número de usuários que votaram para encerrá-la. Se você clicar no link, verá os motivos pelos quais esses usuários selecionaram.
Aaron Bertrand

@whytheq também vê este post recente de Bob Beauchemin . Ele não trata CTE vs. subconsulta especificamente, mas o mesmo tipo de conceito se aplica: se você escolher um padrão não intuitivo por razões de desempenho, documente o que está errado e visite-o novamente para garantir que a manobra que você descobriu ainda seja real. Eu posso até sugerir que a versão mais natural da consulta seja comentada, a menos que você tenha um sistema de controle de origem confiável que mantenha a versão anterior.
Aaron Bertrand


19

#temp é materializado e o CTE não.

CTE é apenas sintaxe, portanto, em teoria, é apenas uma subconsulta. É executado. #temp é materializado. Portanto, um CTE caro em uma junção executada muitas vezes pode ser melhor em uma #temp. Por outro lado, se é uma avaliação fácil que não é executada, mas algumas vezes não vale a sobrecarga de #temp.

Existem algumas pessoas no SO que não gostam de variáveis ​​de tabela, mas eu gosto delas porque elas são materializadas e mais rápidas de criar do que #temp. Há momentos em que o otimizador de consulta se sai melhor com uma #temp em comparação com uma variável de tabela.

A capacidade de criar uma PK em uma variável #temp ou table fornece ao otimizador de consulta mais informações que uma CTE (como você não pode declarar uma PK em uma CTE).


qual é o acrônimo "TVP" ... algo semelhante a #temp?
whytheq

TVP está se tornando um termo comum, porque soa impressionante (para alguns). Em resumo, uma TVP é uma tabela passada como parâmetro. Qualquer pessoa que tenha usado variáveis ​​de tabela estará em casa com elas.
WonderWorker

1
AVISO - Os TVPs não têm planos de execução! Não use TVPs para outra coisa que não seja a mais simples das listas curtas de pesquisa. Se você fizer junções, inserções ou atualizações complexas, poderá encontrar grandes problemas de otimização. Confie em mim, eu fui queimado por isso.
Heliac 29/10/19

12

Apenas duas coisas que eu acho que tornam SEMPRE preferível usar uma # Temp Table em vez de uma CTE são:

  1. Você não pode colocar uma chave primária em um CTE, para que os dados acessados ​​pelo CTE tenham que percorrer cada um dos índices nas tabelas do CTE, em vez de acessar o PK ou Index na tabela temporária.

  2. Como você não pode adicionar restrições, índices e chaves primárias a um CTE, eles são mais propensos a erros de entrada e erros.


-Onedaywhen ontem

Aqui está um exemplo em que restrições de #table podem impedir dados incorretos, o que não é o caso nos CTEs.

DECLARE @BadData TABLE ( 
                       ThisID int
                     , ThatID int );
INSERT INTO @BadData
       ( ThisID
       , ThatID
       ) 
VALUES
       ( 1, 1 ),
       ( 1, 2 ),
       ( 2, 2 ),
       ( 1, 1 );

IF OBJECT_ID('tempdb..#This') IS NOT NULL
    DROP TABLE #This;
CREATE TABLE #This ( 
             ThisID int NOT NULL
           , ThatID int NOT NULL
                        UNIQUE(ThisID, ThatID) );
INSERT INTO #This
SELECT * FROM @BadData;
WITH This_CTE
     AS (SELECT *
           FROM @BadData)
     SELECT *
       FROM This_CTE;

3
ALWAYSé um pouco longe demais, mas obrigado pela resposta. Em termos de legibilidade, o uso de CTEs pode ser uma coisa boa.
whytheq

3
Eu não entendo o seu segundo ponto. Na minha opinião, a consulta que define o CTE é análoga às restrições que você colocaria na tabela temporária, observando que o primeiro pode compreender predicados arbitrariamente complexos, enquanto o último é muito mais limitado (por exemplo, a CHECKrestrição referente a várias linhas / tabelas é não permitido). Você pode postar um exemplo em que um CTE exibe um erro que a tabela temporária equivalente não possui?
precisa saber é o seguinte
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.