Quando devo usar uma variável de tabela versus tabela temporária no servidor sql?


298

Estou aprendendo mais detalhes na variável de tabela. Ele diz que as tabelas temporárias estão sempre no disco e as variáveis ​​da tabela estão na memória, ou seja, o desempenho da variável da tabela é melhor que a tabela temporária porque a variável da tabela usa menos operações de E / S do que a tabela temporária.

Mas, às vezes, se houver muitos registros em uma variável da tabela que não possam estar contidos na memória, a variável da tabela será colocada em disco como a tabela temporária.

Mas não sei o que são "muitos registros". 100.000 registros? ou 1000.000 registros? Como posso saber se uma variável de tabela que estou usando está na memória ou no disco? Existe alguma função ou ferramenta no SQL Server 2005 para medir a escala da variável da tabela ou me avisar quando a variável da tabela é colocada no disco a partir da memória?


5
Uma variável de tabela está quase sempre presente tempDB- que "na memória" é um mito. Além disso: as variáveis ​​de tabela sempre serão consideradas pelo otimizador de consultas como contendo exatamente uma linha - se você tiver muito mais, isso pode levar a planos de execução muito ruins.
marc_s


2
@marc_s - Você pode soltar o "quase" nessa declaração. É sempre tempdb(mas também pode ser inteiramente em memória)
Martin Smith

2
Com o SQL 2014, agora você pode criar uma variável de tabela na memória
paparazzo

Respostas:


362

Sua pergunta mostra que você sucumbiu a alguns dos equívocos comuns sobre variáveis ​​de tabela e tabelas temporárias.

Eu escrevi uma resposta bastante extensa no site do DBA, analisando as diferenças entre os dois tipos de objetos. Isso também aborda sua pergunta sobre disco x memória (não vi nenhuma diferença significativa de comportamento entre os dois).

Em relação à pergunta no título, porém, sobre quando usar uma variável de tabela versus uma tabela temporária local, você nem sempre tem uma opção. Em funções, por exemplo, só é possível usar uma variável de tabela e, se você precisar gravar na tabela em um escopo filho, somente uma #temptabela o fará (parâmetros com valor de tabela permitem acesso somente leitura ).

Onde você pode escolher, algumas sugestões estão abaixo (embora o método mais confiável seja simplesmente testar os dois com sua carga de trabalho específica).

  1. Se você precisar de um índice que não possa ser criado em uma variável de tabela, é claro que precisará de uma #temporarytabela. Os detalhes disso dependem da versão, no entanto. Para o SQL Server 2012 e abaixo os únicos índices que poderiam ser criados na tabela variáveis foram aqueles implicitamente criados através de uma UNIQUEou PRIMARY KEYrestrição. O SQL Server 2014 introduziu a sintaxe de índice embutido para um subconjunto das opções disponíveis em CREATE INDEX. Isso foi estendido desde então para permitir condições de índice filtradas. No INCLUDEentanto, ainda não é possível criar índices com colunas -d ou columnstorestore.

  2. Se você incluir e excluir repetidamente grandes números de linhas da tabela, use uma #temporarytabela. Isso suporta TRUNCATE(que é mais eficiente do que DELETEpara tabelas grandes) e inserções adicionais subsequentes após a TRUNCATEpodem ter um desempenho melhor do que as que seguem a DELETE conforme ilustrado aqui .

  3. Se você estiver excluindo ou atualizando um grande número de linhas, a tabela temporária poderá ter um desempenho muito melhor que uma variável da tabela - se for capaz de usar o compartilhamento de conjunto de linhas (consulte "Efeitos do compartilhamento de conjunto de linhas" abaixo para obter um exemplo).
  4. Se o plano ideal usando a tabela variar dependendo dos dados, use uma #temporarytabela. Isso suporta a criação de estatísticas que permitem que o plano seja recompilado dinamicamente de acordo com os dados (embora para tabelas temporárias em cache em procedimentos armazenados, o comportamento da recompilação precise ser entendido separadamente).
  5. Se é improvável que o plano ideal para a consulta que usa a tabela seja alterado, considere uma variável da tabela para ignorar a sobrecarga da criação e recompilação de estatísticas (possivelmente exigiria dicas para corrigir o plano desejado).
  6. Se a origem dos dados inseridos na tabela for de uma SELECTdeclaração potencialmente cara , considere que o uso de uma variável de tabela bloqueará a possibilidade disso usando um plano paralelo.
  7. Se você precisar que os dados na tabela sobrevivam a uma reversão de uma transação de usuário externo, use uma variável da tabela. Um possível caso de uso para isso pode estar registrando o progresso de diferentes etapas em um lote SQL longo.
  8. Ao usar uma #temptabela dentro de uma transação de usuário, os bloqueios podem ser mantidos por mais tempo do que para as variáveis ​​da tabela (potencialmente até o final da transação versus o final da instrução, dependendo do tipo de nível de bloqueio e isolamento) e também podem impedir o truncamento do tempdblog de transações até o a transação do usuário termina. Portanto, isso pode favorecer o uso de variáveis ​​de tabela.
  9. Nas rotinas armazenadas, as variáveis ​​de tabela e as tabelas temporárias podem ser armazenadas em cache. A manutenção de metadados para variáveis ​​da tabela em cache é menor que a das #temporarytabelas. Bob Ward ressalta em sua tempdbapresentação que isso pode causar contenção adicional nas tabelas do sistema em condições de alta simultaneidade. Além disso, ao lidar com pequenas quantidades de dados, isso pode fazer uma diferença mensurável no desempenho .

Efeitos do compartilhamento de conjunto de linhas

DECLARE @T TABLE(id INT PRIMARY KEY, Flag BIT);

CREATE TABLE #T (id INT PRIMARY KEY, Flag BIT);

INSERT INTO @T 
output inserted.* into #T
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID), 0
FROM master..spt_values v1, master..spt_values v2

SET STATISTICS TIME ON

/*CPU time = 7016 ms,  elapsed time = 7860 ms.*/
UPDATE @T SET Flag=1;

/*CPU time = 6234 ms,  elapsed time = 7236 ms.*/
DELETE FROM @T

/* CPU time = 828 ms,  elapsed time = 1120 ms.*/
UPDATE #T SET Flag=1;

/*CPU time = 672 ms,  elapsed time = 980 ms.*/
DELETE FROM #T

DROP TABLE #T

2
Olá, senhor Martin Smith. No mi case, eu só quero armazenar um conjunto de valores de IDs para usá-los em outras consultas dentro do procedimento Store. Então o que você me recomenda?
Jeancarlo Fontalvo

@JeancarloFontalvo - uma variável de tabela com uma chave primária ativada ide o uso de OPTION (RECOMPILE)provavelmente seriam boas para isso - mas teste as duas.
Martin Smith

a contenção de metadados é a mesma para tabela temporária e variável de tabela?
Syed Aqeel Ashiq 06/04

@Syed. Geralmente menos para TV. Bloqueios podem ser liberados mais cedo se dentro de uma transação do usuário. Veja também o link Bob Ward.
Martin Smith

73

Use uma variável de tabela para uma quantidade muito pequena de dados (milhares de bytes)

Use uma tabela temporária para muitos dados

Outra maneira de pensar sobre isso: se você acha que pode se beneficiar de um índice, estatísticas automatizadas ou qualquer benefício do otimizador de SQL, seu conjunto de dados provavelmente é muito grande para uma variável de tabela.

No meu exemplo, eu só queria colocar cerca de 20 linhas em um formato e modificá-las como um grupo, antes de usá-las para UPDATE / INSERT uma tabela permanente. Portanto, uma variável de tabela é perfeita.

Mas também estou executando o SQL para preencher milhares de linhas por vez e, definitivamente, posso dizer que as tabelas temporárias têm um desempenho muito melhor que as variáveis ​​da tabela.

Isso não é diferente de como as CTEs são uma preocupação por um motivo de tamanho semelhante - se os dados na CTE forem muito pequenos, acho que a CTE tem um desempenho tão bom ou melhor do que o otimizador, mas se for muito grande, isso te machuca muito.

Meu entendimento é baseado principalmente em http://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/ , que possui muito mais detalhes.


A variável takeaway is table é adequada para conjuntos de dados pequenos, mas use a tabela temporária para conjuntos de dados maiores. Eu tenho uma consulta com milhares de linhas. Ao alternar da variável de tabela para a tabela temporária, o tempo de consulta diminui de 40 para apenas 5, com todo o resto sendo igual.
liang

42

A Microsoft diz aqui

As variáveis ​​da tabela não possuem estatísticas de distribuição, elas não acionam recompilações. Portanto, em muitos casos, o otimizador criará um plano de consulta supondo que a variável da tabela não tenha linhas. Por esse motivo, você deve ter cuidado ao usar uma variável de tabela se espera um número maior de linhas (maior que 100). As tabelas temporárias podem ser uma solução melhor nesse caso.


14

Eu concordo totalmente com Abacus (desculpe - não tenho pontos suficientes para comentar).

Além disso, lembre-se de que não se trata necessariamente de quantos registros você possui, mas do tamanho dos seus registros.

Por exemplo, você considerou a diferença de desempenho entre 1.000 registros com 50 colunas cada vs 100.000 registros com apenas 5 colunas cada?

Por fim, talvez você esteja consultando / armazenando mais dados do que precisa? Aqui está uma boa leitura sobre estratégias de otimização de SQL . Limite a quantidade de dados que você está obtendo, especialmente se você não estiver usando tudo (alguns programadores SQL ficam preguiçosos e apenas selecionam tudo, mesmo que usem apenas um pequeno subconjunto). Não esqueça que o analisador de consultas SQL também pode se tornar seu melhor amigo.


4

A tabela variável está disponível apenas para a sessão atual. Por exemplo, se você precisar de EXECoutro procedimento armazenado na atual, precisará passar pela tabela, pois é Table Valued Parameterclaro que isso afetará o desempenho. Com tabelas temporárias, você pode fazer isso apenas passando o nome da tabela temporária

Para testar uma tabela temporária:

  • Editor de consultas do studio de gerenciamento aberto
  • Crie uma tabela temporária
  • Abra outra janela do editor de consultas
  • Selecione nesta tabela "Disponível"

Para testar uma tabela variável:

  • Editor de consultas do studio de gerenciamento aberto
  • Crie uma tabela variável
  • Abra outra janela do editor de consultas
  • Selecione nesta tabela "Não disponível"

outra coisa que experimentei é: se o seu esquema não tiver GRANTprivilégio de criar tabelas, use tabelas variáveis.


3

escrevendo dados em tabelas declaradas declare @tbe depois de me juntar a outras tabelas, percebi que o tempo de resposta comparado às tabelas temporárias tempdb .. # tbé muito maior.

Quando eu os uno ao @tb, o tempo é muito maior para retornar o resultado, ao contrário do #tm , o retorno é quase instantâneo.

Eu fiz testes com uma junção de 10.000 linhas e junção com 5 outras tabelas


Você poderia postar o teste que executou para obter esses números?
Dan Def
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.