Esta parece ser uma área com muitos mitos e visões conflitantes.
Então, qual é a diferença entre uma variável de tabela e uma tabela temporária local no SQL Server?
sql-server
t-sql
temporary-tables
Martin Smith
fonte
fonte
Respostas:
Conteúdo
Embargo
Esta resposta discute variáveis de tabela "clássicas" introduzidas no SQL Server 2000. O SQL Server 2014 na memória OLTP apresenta os tipos de tabela com otimização de memória. As instâncias variáveis de tabela dessas são diferentes em muitos aspectos das discutidas abaixo! ( mais detalhes ).
Local de armazenamento
Não faz diferença. Ambos são armazenados em
tempdb
.Já vi sugerir que, para variáveis de tabela, esse nem sempre é o caso, mas isso pode ser verificado a seguir
Resultados de exemplo (mostrando a localização nas
tempdb
2 linhas são armazenados)Localização Lógica
@table_variables
se comportam mais como se fizessem parte do banco de dados atual do que as#temp
tabelas. Para variáveis de tabela (desde 2005), os agrupamentos de colunas, se não especificados explicitamente, serão os do banco de dados atual, enquanto para as#temp
tabelas ele usará o agrupamento padrão detempdb
( Mais detalhes ). Além disso, os tipos de dados definidos pelo usuário e as coleções XML devem estar no tempdb para serem usados nas#temp
tabelas, mas as variáveis da tabela podem usá-los no banco de dados atual ( Origem ).O SQL Server 2012 apresenta bancos de dados contidos. o comportamento das tabelas temporárias nessas diferenças (h / t Aaron)
Visibilidade para diferentes escopos
@table_variables
só podem ser acessados no lote e no escopo em que são declarados.#temp_tables
estão acessíveis em lotes filhos (gatilhos aninhados, procedimento,exec
chamadas).#temp_tables
criado no escopo externo (@@NESTLEVEL=0
) também pode abranger lotes, pois persistem até o término da sessão. Nenhum tipo de objeto pode ser criado em um lote filho e acessado no escopo de chamada, como discutido a seguir ( podem ser encontradas##temp
tabelas globais ).Tempo de vida
@table_variables
são criados implicitamente quando um lote contendo umaDECLARE @.. TABLE
instrução é executada (antes da execução de qualquer código de usuário) e são descartados implicitamente no final.Embora o analisador não permita que você tente usar a variável de tabela antes da
DECLARE
instrução, a criação implícita pode ser vista abaixo.#temp_tables
são criados explicitamente quando aCREATE TABLE
instrução TSQL é encontrada e podem ser eliminados explicitamente comDROP TABLE
ou serão descartados implicitamente quando o lote termina (se criado em um lote filho com@@NESTLEVEL > 0
) ou quando a sessão termina de outra forma.Nota: nas rotinas armazenadas, ambos os tipos de objetos podem ser armazenados em cache, em vez de criar e soltar repetidamente novas tabelas. Existem restrições sobre quando esse cache pode ocorrer, no entanto, é possível violar,
#temp_tables
mas as restrições@table_variables
impedem de qualquer maneira. A sobrecarga de manutenção para#temp
tabelas em cache é um pouco maior que para as variáveis da tabela, conforme ilustrado aqui .Metadados do objeto
Isso é essencialmente o mesmo para os dois tipos de objeto. Ele é armazenado nas tabelas base do sistema em
tempdb
. No#temp
entanto, é mais simples ver uma tabela, pois elaOBJECT_ID('tempdb..#T')
pode ser usada para digitar as tabelas do sistema e o nome gerado internamente está mais correlacionado com o nome definido naCREATE TABLE
instrução. Para variáveis de tabela, aobject_id
função não funciona e o nome interno é inteiramente gerado pelo sistema, sem relação com o nome da variável. A seguir, demonstramos que os metadados ainda estão lá, digitando um nome de coluna (possivelmente único). Para tabelas sem nomes de colunas exclusivos, o object_id pode ser determinado usando-seDBCC PAGE
desde que não estejam vazios.Resultado
Transações
As operações
@table_variables
são executadas como transações do sistema, independentemente de qualquer transação do usuário externo, enquanto as#temp
operações de tabela equivalentes são executadas como parte da própria transação do usuário. Por esse motivo, umROLLBACK
comando afetará uma#temp
tabela, mas deixará o@table_variable
intocado.Exploração madeireira
Ambos geram registros de log para o
tempdb
log de transações. Um equívoco comum é que esse não é o caso de variáveis de tabela; portanto, um script demonstrando isso está abaixo; ele declara uma variável de tabela, adiciona algumas linhas, as atualiza e as exclui.Como a variável da tabela é criada e eliminada implicitamente no início e no final do lote, é necessário usar vários lotes para ver o log completo.
Devoluções
Visão detalhada
Visualização de resumo (inclui o log para queda implícita e tabelas base do sistema)
Tanto quanto eu sou capaz de discernir as operações em ambos, geramos quantidades aproximadamente iguais de log.
Embora a quantidade de log seja muito semelhante, uma diferença importante é que os registros de log relacionados às
#temp
tabelas não podem ser limpos até que qualquer transação de usuário que contenha seja concluída, portanto, uma transação de longa execução que em algum momento grava em#temp
tabelas impedirá o truncamento de log,tempdb
enquanto as transações autônomas gerado para variáveis de tabela não.As variáveis de tabela não suportam,
TRUNCATE
portanto, podem estar em desvantagem de registro quando o requisito é remover todas as linhas de uma tabela (embora para tabelas muito pequenasDELETE
possam funcionar melhor de qualquer maneira )Cardinalidade
Muitos dos planos de execução que envolvem variáveis de tabela mostrarão uma única linha estimada como a saída deles. A inspeção das propriedades da variável da tabela mostra que o SQL Server acredita que a variável da tabela possui zero linhas (por que ela estima que 1 linha será emitida a partir de uma tabela com zero linhas é explicada por @Paul White aqui ).
No entanto, os resultados mostrados na seção anterior mostram uma
rows
contagem precisa emsys.partitions
. O problema é que, na maioria das vezes, as instruções que referenciam as variáveis da tabela são compiladas enquanto a tabela está vazia. Se a declaração for (re) compilada depois de@table_variable
preenchida, será usada para a cardinalidade da tabela (isso pode acontecer devido a um explícitorecompile
ou talvez porque a declaração também faça referência a outro objeto que causa uma compilação adiada ou uma recompilação.)O plano mostra a contagem precisa estimada de linhas após a compilação adiada.
No SQL Server 2012 SP2, o sinalizador de rastreamento 2453 é introduzido. Mais detalhes estão em "Relational Engine" aqui .
Quando esse sinalizador de rastreamento está ativado, pode fazer com que as recompilações automáticas levem em consideração a cardinalidade alterada, conforme discutido mais em breve.
Nota: No Azure, no nível de compatibilidade 150, a compilação da instrução agora é adiada até a primeira execução . Isso significa que ele não estará mais sujeito ao problema de estimativa de linha zero.
Nenhuma estatística de coluna
Ter uma cardinalidade de tabela mais precisa, no entanto, não significa que a contagem estimada de linhas seja mais precisa (a menos que seja realizada uma operação em todas as linhas da tabela). O SQL Server não mantém estatísticas de coluna para variáveis de tabela, portanto, recorrerá a estimativas baseadas no predicado de comparação (por exemplo, 10% da tabela será retornada para uma
=
coluna não exclusiva ou 30% para uma>
comparação). Em contraste, as estatísticas da coluna são mantidas para#temp
tabelas.O SQL Server mantém uma contagem do número de modificações feitas em cada coluna. Se o número de modificações desde a compilação do plano exceder o limite de recompilação (RT), o plano será recompilado e as estatísticas atualizadas. O RT depende do tipo e tamanho da tabela.
Do cache de plano no SQL Server 2008
a
KEEP PLAN
dica pode ser usada para definir o RT para#temp
tabelas da mesma forma que para tabelas permanentes.O efeito líquido de tudo isso é que geralmente os planos de execução gerados para
#temp
tabelas são melhores em magnitude do que@table_variables
quando há muitas linhas envolvidas, já que o SQL Server tem melhores informações para trabalhar.NB1: As variáveis de tabela não possuem estatísticas, mas ainda podem incorrer em um evento de recompilação "Estatísticas Alteradas" no sinalizador de rastreamento 2453 (não se aplica a planos "triviais"). Isso parece ocorrer sob os mesmos limites de recompilação, como mostrado para as tabelas temporárias acima com um mais um que se
N=0 -> RT = 1
. isto é, todas as instruções compiladas quando a variável da tabela está vazia acabam recebendo uma recompilação e são corrigidasTableCardinality
na primeira vez em que são executadas quando não estão vazias. A cardinalidade da tabela de tempos de compilação é armazenada no plano e se a instrução for executada novamente com a mesma cardinalidade (devido ao fluxo de instruções de controle ou à reutilização de um plano em cache), nenhuma recompilação ocorrerá.NB2: Para tabelas temporárias em cache em procedimentos armazenados, a história de recompilação é muito mais complicada do que a descrita acima. Consulte Tabelas temporárias em procedimentos armazenados para obter todos os detalhes sangrentos.
Recompiles
Assim como as recompilações baseadas em modificação descritas acima, as
#temp
tabelas também podem ser associadas a compilações adicionais, simplesmente porque elas permitem operações proibidas para variáveis de tabela que acionam uma compilação (por exemploCREATE INDEX
, alterações de DDLALTER TABLE
)Travamento
Foi afirmado que as variáveis da tabela não participam do bloqueio. Este não é o caso. Executando as saídas abaixo na guia Mensagens do SSMS, os detalhes dos bloqueios obtidos e liberados para uma instrução de inserção.
Para consultas que, a
SELECT
partir das variáveis da tabela, Paul White aponta nos comentários que elas vêm automaticamente com umaNOLOCK
dica implícita . Isso é mostrado abaixoResultado
O impacto disso no bloqueio pode ser bem menor, no entanto.
Nenhum desses retornos resulta na ordem das chaves de índice, indicando que o SQL Server usou uma verificação ordenada de alocação para ambos.
Eu executei o script acima duas vezes e os resultados para a segunda execução estão abaixo
A saída de bloqueio para a variável da tabela é realmente extremamente mínima, pois o SQL Server apenas adquire um bloqueio de estabilidade do esquema no objeto. Mas, para uma
#temp
mesa, é quase tão leve quanto é necessário umS
bloqueio no nível do objeto . UmaNOLOCK
dica ouREAD UNCOMMITTED
nível de isolamento pode, naturalmente, ser especificado explicitamente quando se trabalha com#temp
mesas bem.Da mesma forma que o problema com o log de uma transação do usuário ao redor, isso pode significar que os bloqueios são mantidos por mais tempo nas
#temp
tabelas. Com o script abaixoQuando executado fora de uma transação explícita do usuário nos dois casos, o único bloqueio retornado ao verificar
sys.dm_tran_locks
é um bloqueio compartilhado noDATABASE
.Ao descomentar, as
BEGIN TRAN ... ROLLBACK
26 linhas são retornadas, mostrando que os bloqueios são mantidos no próprio objeto e nas linhas da tabela do sistema para permitir a reversão e impedir que outras transações leiam dados não confirmados. A operação de variável de tabela equivalente não está sujeita a reversão com a transação do usuário e não precisa reter esses bloqueios para que possamos verificar na próxima instrução, mas os bloqueios de rastreamento adquiridos e liberados no Profiler ou usando o sinalizador de rastreamento 1200 mostram muitos eventos de bloqueio ainda ocorrer.Índices
Para versões anteriores ao SQL Server 2014, os índices só podem ser criados implicitamente nas variáveis da tabela como efeito colateral da adição de uma restrição exclusiva ou chave primária. Obviamente, isso significa que apenas índices únicos são suportados. Um índice não clusterizado não exclusivo em uma tabela com um índice clusterizado exclusivo pode ser simulado, simplesmente declarando-o
UNIQUE NONCLUSTERED
e adicionando a chave CI ao final da chave NCI desejada (o SQL Server faria isso nos bastidores de qualquer maneira, mesmo se um não exclusivo NCI pode ser especificado)Como demonstrado anteriormente, vários
index_option
s podem ser especificados na declaração de restriçãoDATA_COMPRESSION
, incluindo,,IGNORE_DUP_KEY
eFILLFACTOR
(embora não haja sentido em defini-lo, pois isso só faria diferença na reconstrução do índice e você não pode reconstruir índices nas variáveis da tabela!)Além disso, as variáveis de tabela não suportam
INCLUDE
d colunas, índices filtrados (até 2016) ou particionamento, mas as#temp
tabelas (o esquema de partição deve ser criadotempdb
).Índices no SQL Server 2014
Índices não exclusivos podem ser declarados embutidos na definição de variável da tabela no SQL Server 2014. Exemplo de sintaxe para isso é mostrado abaixo.
Índices no SQL Server 2016
A partir do CTP 3.1, agora é possível declarar índices filtrados para variáveis de tabela. Por RTM, pode ser que as colunas incluídas também sejam permitidas, embora elas provavelmente não entrem no SQL16 devido a restrições de recursos
Paralelismo
As consultas inseridas (ou modificadas)
@table_variables
não podem ter um plano paralelo,#temp_tables
não são restritas dessa maneira.Existe uma solução alternativa aparente nessa reescrita, da seguinte forma, permitindo que a
SELECT
peça ocorra paralelamente, mas isso acaba usando uma tabela temporária oculta (nos bastidores)Não existe tal limitação nas consultas que selecionam as variáveis da tabela, conforme ilustrado na minha resposta aqui
Outras diferenças funcionais
#temp_tables
não pode ser usado dentro de uma função.@table_variables
pode ser usado dentro de UDFs escalares ou de tabelas com várias instruções.@table_variables
não pode ter restrições nomeadas.@table_variables
não pode serSELECT
-edINTO
,ALTER
-ed,TRUNCATE
d ou ser o destino deDBCC
comandos comoDBCC CHECKIDENT
ou deSET IDENTITY INSERT
e não suporta dicas de tabela comoWITH (FORCESCAN)
CHECK
as restrições nas variáveis da tabela não são consideradas pelo otimizador para simplificação, predicados implícitos ou detecção de contradições.PAGELATCH_EX
espera. ( Exemplo )Apenas memória?
Conforme declarado no início, ambos são armazenados nas páginas da
tempdb
. No entanto, não resolvi se havia alguma diferença de comportamento quando se trata de gravar essas páginas em disco.Eu fiz uma pequena quantidade de testes sobre isso agora e até agora não vi essa diferença. No teste específico que fiz na minha instância do SQL Server 250 páginas, parece ser o ponto de corte antes que o arquivo de dados seja gravado.
Executando o script abaixo
E o monitoramento de gravações no
tempdb
arquivo de dados com o Process Monitor não vi nenhum (exceto ocasionalmente na página de inicialização do banco de dados no deslocamento 73.728). Depois de mudar250
para251
eu comecei a ver as gravações abaixo.A captura de tela acima mostra gravações de 5 * 32 páginas e uma única página, indicando que 161 das páginas foram gravadas no disco. Também obtive o mesmo ponto de corte de 250 páginas ao testar com variáveis de tabela. O script abaixo mostra uma maneira diferente, observando
sys.dm_os_buffer_descriptors
Resultados
Mostrando que 192 páginas foram gravadas no disco e o sinalizador sujo foi limpo. Também mostra que a gravação em disco não significa que as páginas serão removidas do buffer pool imediatamente. As consultas nessa variável de tabela ainda podem ser satisfeitas inteiramente da memória.
Em um servidor inativo com páginas de
max server memory
conjunto de buffers definidas2000 MB
eDBCC MEMORYSTATUS
relatadas como aproximadamente 1.843.000 KB (c. 23.000 páginas), inseri as tabelas acima em lotes de 1.000 linhas / páginas e para cada iteração registrada.Tanto a variável da tabela quanto a
#temp
tabela forneceram gráficos quase idênticos e conseguiram maximizar o buffer pool antes de chegar ao ponto em que elas não estavam totalmente armazenadas na memória, de modo que não parece haver nenhuma limitação específica quanto à quantidade de memória qualquer um pode consumir.fonte
Gostaria de salientar algumas coisas com base mais em experiências particulares do que em estudos. Como DBA, sou muito novo, por isso, corrija-me quando necessário.
fonte