Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Quando devo usar uma variável de tabela vs tabela temporária no sql server?


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).

  1. 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 um UNIQUE ou PRIMARY KEY limitaçã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. Índices com INCLUDE -d colunas ou índices columnstore ainda não são possíveis de criar em variáveis ​​de tabela.

  2. Se você estiver adicionando e excluindo repetidamente um grande número de linhas da tabela, use um #temporary tabela. Que suporta TRUNCATE (que é mais eficiente que DELETE para tabelas grandes) e inserções subsequentes adicionais seguindo um TRUNCATE podem ter um desempenho melhor do que aqueles que seguem um DELETE como ilustrado aqui.
  3. 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) .
  4. 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).
  5. 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).
  6. >
  7. 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.
  8. 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.
  9. 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 do tempdb log de transações até que a transação do usuário termine. Portanto, isso pode favorecer o uso de variáveis ​​de tabela.
  10. 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 seu tempdb 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