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 #temp
tabela 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).
Se você precisar de um índice que não possa ser criado em uma variável de tabela, é claro que precisará de uma #temporary
tabela. 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 UNIQUE
ou PRIMARY KEY
restriçã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 INCLUDE
entanto, ainda não é possível criar índices com colunas -d ou columnstorestore.
Se você incluir e excluir repetidamente grandes números de linhas da tabela, use uma #temporary
tabela. Isso suporta TRUNCATE
(que é mais eficiente do que DELETE
para tabelas grandes) e inserções adicionais subsequentes após a TRUNCATE
podem ter um desempenho melhor do que as que seguem a DELETE
conforme ilustrado aqui .
- 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).
- Se o plano ideal usando a tabela variar dependendo dos dados, use uma
#temporary
tabela. 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).
- 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).
- Se a origem dos dados inseridos na tabela for de uma
SELECT
declaração potencialmente cara , considere que o uso de uma variável de tabela bloqueará a possibilidade disso usando um plano paralelo.
- 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.
- Ao usar uma
#temp
tabela 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 tempdb
log de transações até o a transação do usuário termina. Portanto, isso pode favorecer o uso de variáveis de tabela.
- 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
#temporary
tabelas. Bob Ward ressalta em sua tempdb
apresentaçã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
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.