Sua pergunta mostra que você sucumbiu a alguns dos equívocos comuns em torno de 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 objeto. Isso também aborda sua pergunta sobre disco versus memória (não vi nenhuma diferença significativa no 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 escolha. Em funções, por exemplo, só é possível usar uma variável de tabela e se você precisar escrever na tabela em um escopo filho então apenas um
#temp
table fará (parâmetros com valor de tabela permitem acesso somente leitura). Onde você tem uma escolha, algumas sugestões estão abaixo (embora o método mais confiável seja simplesmente testar ambos com sua carga de trabalho específica).
-
Se você precisar de um índice que não pode ser criado em uma variável de tabela, é claro que precisará de um#temporary
tabela. No entanto, os detalhes disso dependem da versão. Para SQL Server 2012 e abaixo, os únicos índices que podiam ser criados em variáveis de tabela eram aqueles criados implicitamente por meio de umUNIQUE
ouPRIMARY KEY
limitação. O SQL Server 2014 introduziu a sintaxe de índice embutido para um subconjunto das opções disponíveis emCREATE INDEX
. Isso foi estendido desde então para permitir condições de índice filtradas. Índices comINCLUDE
-d colunas ou índices columnstore ainda não são possíveis de criar em variáveis de tabela.
-
Se você estiver adicionando e excluindo repetidamente um grande número de linhas da tabela, use um#temporary
tabela. Que suportaTRUNCATE
(que é mais eficiente queDELETE
para tabelas grandes) e inserções subsequentes adicionais seguindo umTRUNCATE
podem ter um desempenho melhor do que aqueles que seguem umDELETE
como ilustrado aqui.
- Se você estiver excluindo ou atualizando um grande número de linhas, a tabela temporária pode ter um desempenho muito melhor do que uma variável de tabela - se puder 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 um
#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 de recompilação precise ser entendido separadamente). - Se for improvável que o plano ideal para a consulta usando a tabela seja alterado, considere uma variável de tabela para ignorar a sobrecarga de criação de estatísticas e recompilações (possivelmente exigiria dicas para corrigir o plano desejado). >
- Se a fonte dos dados inseridos na tabela for de um
SELECT
potencialmente caro Em seguida, considere que usar uma variável de tabela bloqueará a possibilidade de usar um plano paralelo. - Se você precisar dos dados na tabela para sobreviver a uma reversão de uma transação de usuário externa, use uma variável de tabela. Um possível caso de uso para isso pode ser registrar o progresso de diferentes etapas em um lote SQL longo.
- Ao usar um
#temp
tabela dentro de uma transação de usuário, os bloqueios podem ser mantidos por mais tempo do que para variáveis de tabela (potencialmente até o final da transação versus final da instrução, dependendo do tipo de bloqueio e nível de isolamento) e também pode impedir o truncamento dotempdb
log de transações até que a transação do usuário termine. Portanto, isso pode favorecer o uso de variáveis de tabela. - Dentro de rotinas armazenadas, tanto as variáveis de tabela quanto as tabelas temporárias podem ser armazenadas em cache. A manutenção de metadados para variáveis de tabela em cache é menor do que para
#temporary
mesas. Bob Ward aponta em seutempdb
apresentação de que isso pode causar contenção adicional nas tabelas do sistema sob 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 do 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