Qual é a diferença entre uma tabela temporária e uma variável de tabela no SQL Server?

390

No SQL Server 2005, podemos criar tabelas temporárias de duas maneiras:

declare @tmp table (Col1 int, Col2 int);

ou

create table #tmp (Col1 int, Col2 int);

Quais são as diferenças entre esses dois? Eu li opiniões conflitantes sobre se o @tmp ainda usa tempdb ou se tudo acontece na memória.

Em quais cenários um supera o outro?

Eric Z Beard
fonte
2
Existe uma redação muito boa de Pinal Dave aqui ... blog.sqlauthority.com/2009/12/15/…
sam yi

Respostas:

392

Existem algumas diferenças entre Tabelas Temporárias (#tmp) e Variáveis ​​de Tabela (@tmp), embora o uso do tempdb não seja uma delas, conforme descrito no link do MSDN abaixo.

Como regra geral, para volumes pequenos e médios de dados e cenários de uso simples, você deve usar variáveis ​​de tabela. (Esta é uma diretriz excessivamente ampla, com muitas exceções, é claro - veja abaixo e nos artigos a seguir.)

Alguns pontos a serem considerados ao escolher entre eles:

  • Tabelas temporárias são tabelas reais para que você possa fazer coisas como CREATE INDEXes, etc. Se você tiver grandes quantidades de dados para as quais o acesso por índice será mais rápido, as tabelas temporárias são uma boa opção.

  • As variáveis ​​da tabela podem ter índices usando as restrições PRIMARY KEY ou UNIQUE. (Se você deseja um índice não exclusivo, inclua a coluna de chave primária como a última coluna na restrição exclusiva. Se você não tiver uma coluna exclusiva, poderá usar uma coluna de identidade.) O SQL 2014 também possui índices não exclusivos. .

  • Variáveis ​​de tabela não participam de transações SELECTes estão implicitamente com NOLOCK. O comportamento da transação pode ser muito útil, por exemplo, se você deseja ROLLBACK no meio de um procedimento, as variáveis ​​de tabela preenchidas durante essa transação ainda serão preenchidas!

  • As tabelas temporárias podem resultar na recompilação dos procedimentos armazenados, talvez com frequência. Variáveis ​​de tabela não.

  • Você pode criar uma tabela temporária usando SELECT INTO, que pode ser mais rápido de escrever (bom para consultas ad-hoc) e pode permitir que você lide com a alteração dos tipos de dados ao longo do tempo, pois não é necessário definir a estrutura da tabela temporária antecipadamente.

  • Você pode passar variáveis ​​de tabela de volta das funções, permitindo encapsular e reutilizar a lógica com muito mais facilidade (por exemplo, torne uma função dividir uma string em uma tabela de valores em algum delimitador arbitrário).

  • O uso de variáveis ​​de tabela em funções definidas pelo usuário permite que essas funções sejam usadas mais amplamente (consulte a documentação de CREATE FUNCTION para obter detalhes). Se você estiver escrevendo uma função, use variáveis ​​de tabela sobre tabelas temporárias, a menos que exista uma necessidade atraente.

  • As variáveis ​​de tabela e as tabelas temporárias são armazenadas no tempdb. Porém, as variáveis ​​de tabela (desde 2005) são padronizadas para o agrupamento do banco de dados atual versus as tabelas temporárias, que recebem o agrupamento padrão do tempdb ( ref ). Isso significa que você deve estar ciente dos problemas de agrupamento se o uso de tabelas temporárias e seu agrupamento de banco de dados for diferente do tempdb, causando problemas se você quiser comparar dados na tabela temporária com dados do banco de dados.

  • As tabelas temporárias globais (## tmp) são outro tipo de tabela temporária disponível para todas as sessões e usuários.

Algumas leituras adicionais:

Rory
fonte
26
Variáveis ​​de tabela podem ter índices. Basta criar uma restrição exclusiva e você obterá automaticamente um índice. Faz uma enorme diferença de desempenho. (Se você não deseja um índice exclusivo, basta adicionar a chave primária real no final dos campos que deseja. Se você não tiver uma, crie uma coluna de identidade).
Ben
7
@ Ben e SQL Server 2014 permite que os índices não exclusivos a ser especificado na tabela variáveis
Martin Smith
4
As variáveis ​​da tabela que não são afetadas pelas transações são úteis algumas vezes. Se você tiver algo que deseja manter após uma reversão, poderá colocá-lo em uma variável de tabela.
quillbreaker
3
As estatísticas são criadas para tabelas temporárias, que podem melhorar os planos de consulta, mas não para variáveis ​​de tabela. Essas estatísticas são armazenadas em cache por um tempo, juntamente com as páginas da tabela temporária, depois que a tabela temporária é descartada e podem ser imprecisas caso a tabela em cache seja reativada.
Michael Green
As variáveis ​​da tabela usarão como padrão o agrupamento do tipo de dados definido pelo usuário (se a coluna for de um tipo de dados definido pelo usuário) ou o agrupamento do banco de dados atual e não o agrupamento padrão do tempdb. As tabelas temporárias usarão o agrupamento padrão tempdb. Consulte: technet.microsoft.com/en-us/library/ms188927.aspx
PseudoToad
25

Basta olhar para a afirmação na resposta aceita que as variáveis ​​da tabela não participam do log.

Parece geralmente falso que exista alguma diferença na quantidade de registro (pelo menos para insert/ update/ deleteoperações na tabela em si, embora eu tenha descoberto que há uma pequena diferença nesse aspecto para objetos temporários em cache nos procedimentos armazenados devido a tabela adicional do sistema atualizações).

Analisei o comportamento do registro em relação a @table_variablee a #temptabela para as seguintes operações.

  1. Inserção bem-sucedida
  2. Inserção de várias linhas onde a instrução foi revertida devido a violação de restrição.
  3. Atualizar
  4. Excluir
  5. Desalocar

Os registros do log de transações eram quase idênticos para todas as operações.

Na verdade, a versão da variável de tabela possui algumas entradas de log extras porque ela é adicionada (e removida posteriormente) à sys.syssingleobjrefstabela base, mas no geral tinha poucos bytes registrados apenas como o nome interno das variáveis ​​da tabela consome 236 bytes a menos do que nas #temptabelas (118 nvarcharcaracteres a menos ).

Script completo para reproduzir (melhor execução em uma instância iniciada no modo de usuário único e usando o sqlcmdmodo)

:setvar tablename "@T" 
:setvar tablescript "DECLARE @T TABLE"

/*
 --Uncomment this section to test a #temp table
:setvar tablename "#T" 
:setvar tablescript "CREATE TABLE #T"
*/

USE tempdb 
GO    
CHECKPOINT

DECLARE @LSN NVARCHAR(25)

SELECT @LSN = MAX([Current LSN])
FROM fn_dblog(null, null) 


EXEC(N'BEGIN TRAN StartBatch
SAVE TRAN StartBatch
COMMIT

$(tablescript)
(
[4CA996AC-C7E1-48B5-B48A-E721E7A435F0] INT PRIMARY KEY DEFAULT 0,
InRowFiller char(7000) DEFAULT ''A'',
OffRowFiller varchar(8000) DEFAULT REPLICATE(''B'',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast(''C'' as varchar(max)),10000)
)


BEGIN TRAN InsertFirstRow
SAVE TRAN InsertFirstRow
COMMIT

INSERT INTO $(tablename)
DEFAULT VALUES

BEGIN TRAN Insert9Rows
SAVE TRAN Insert9Rows
COMMIT


INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP 9 ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM sys.all_columns

BEGIN TRAN InsertFailure
SAVE TRAN InsertFailure
COMMIT


/*Try and Insert 10 rows, the 10th one will cause a constraint violation*/
BEGIN TRY
INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP (10) (10 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))) % 20
FROM sys.all_columns
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH

BEGIN TRAN Update10Rows
SAVE TRAN Update10Rows
COMMIT

UPDATE $(tablename)
SET InRowFiller = LOWER(InRowFiller),
    OffRowFiller  =LOWER(OffRowFiller),
    LOBFiller  =LOWER(LOBFiller)


BEGIN TRAN Delete10Rows
SAVE TRAN Delete10Rows
COMMIT

DELETE FROM  $(tablename)
BEGIN TRAN AfterDelete
SAVE TRAN AfterDelete
COMMIT

BEGIN TRAN EndBatch
SAVE TRAN EndBatch
COMMIT')


DECLARE @LSN_HEX NVARCHAR(25) = 
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 1, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 10, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 19, 4),2) AS INT) AS VARCHAR)        

SELECT 
    [Operation],
    [Context],
    [AllocUnitName],
    [Transaction Name],
    [Description]
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  

SELECT CASE
         WHEN GROUPING(Operation) = 1 THEN 'Total'
         ELSE Operation
       END AS Operation,
       Context,
       AllocUnitName,
       COALESCE(SUM([Log Record Length]), 0) AS [Size in Bytes],
       COUNT(*)                              AS Cnt
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  
GROUP BY GROUPING SETS((Operation, Context, AllocUnitName),())

Resultados

+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
|                       |                    |                           |             @TV      |             #TV      |                  |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Operation             | Context            | AllocUnitName             | Size in Bytes | Cnt  | Size in Bytes | Cnt  | Difference Bytes |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| LOP_ABORT_XACT        | LCX_NULL           |                           | 52            | 1    | 52            | 1    |                  |
| LOP_BEGIN_XACT        | LCX_NULL           |                           | 6056          | 50   | 6056          | 50   |                  |
| LOP_COMMIT_XACT       | LCX_NULL           |                           | 2548          | 49   | 2548          | 49   |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 624           | 3    | 624           | 3    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 208           | 1    | 208           | 1    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrscols.clst        | 832           | 4    | 832           | 4    |                  |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL           |                           | 120           | 3    | 120           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 720           | 9    | 720           | 9    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.clust   | 444           | 3    | 444           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.nc      | 276           | 3    | 276           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.clst       | 628           | 4    | 628           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.nc         | 484           | 4    | 484           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.clst      | 176           | 1    | 176           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.nc        | 144           | 1    | 144           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.clst        | 100           | 1    | 100           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.nc1         | 88            | 1    | 88            | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysobjvalues.clst     | 596           | 5    | 596           | 5    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrowsets.clust      | 132           | 1    | 132           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrscols.clst        | 528           | 4    | 528           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.clst       | 1040          | 6    | 1276          | 6    | 236              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc1        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc2        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc3        | 480           | 6    | 480           | 6    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.clst | 96            | 1    |               |      | -96              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.nc1  | 88            | 1    |               |      | -88              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | Unknown Alloc Unit        | 72092         | 19   | 72092         | 19   |                  |
| LOP_DELETE_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 16348         | 37   | 16348         | 37   |                  |
| LOP_FORMAT_PAGE       | LCX_HEAP           | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_FORMAT_PAGE       | LCX_IAM            | Unknown Alloc Unit        | 252           | 3    | 252           | 3    |                  |
| LOP_FORMAT_PAGE       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 84            | 1    | 84            | 1    |                  |
| LOP_FORMAT_PAGE       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 4788          | 57   | 4788          | 57   |                  |
| LOP_HOBT_DDL          | LCX_NULL           |                           | 108           | 3    | 108           | 3    |                  |
| LOP_HOBT_DELTA        | LCX_NULL           |                           | 9600          | 150  | 9600          | 150  |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 456           | 3    | 456           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syscolpars.clst       | 644           | 4    | 644           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysidxstats.clst      | 180           | 1    | 180           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysiscols.clst        | 104           | 1    | 104           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysobjvalues.clst     | 616           | 5    | 616           | 5    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 136           | 1    | 136           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrscols.clst        | 544           | 4    | 544           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1064          | 6    | 1300          | 6    | 236              |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syssingleobjrefs.clst | 100           | 1    |               |      | -100             |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | Unknown Alloc Unit        | 135888        | 19   | 135888        | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysallocunits.nc      | 288           | 3    | 288           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syscolpars.nc         | 500           | 4    | 500           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysidxstats.nc        | 148           | 1    | 148           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysiscols.nc1         | 92            | 1    | 92            | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc1        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc2        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc3        | 504           | 6    | 504           | 6    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syssingleobjrefs.nc1  | 92            | 1    |               |      | -92              |
| LOP_INSERT_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 5112          | 71   | 5112          | 71   |                  |
| LOP_MARK_SAVEPOINT    | LCX_NULL           |                           | 508           | 8    | 508           | 8    |                  |
| LOP_MODIFY_COLUMNS    | LCX_CLUSTERED      | Unknown Alloc Unit        | 1560          | 10   | 1560          | 10   |                  |
| LOP_MODIFY_HEADER     | LCX_HEAP           | Unknown Alloc Unit        | 3780          | 45   | 3780          | 45   |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.syscolpars.clst       | 384           | 4    | 384           | 4    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysidxstats.clst      | 100           | 1    | 100           | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysrowsets.clust      | 92            | 1    | 92            | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1144          | 13   | 1144          | 13   |                  |
| LOP_MODIFY_ROW        | LCX_IAM            | Unknown Alloc Unit        | 4224          | 48   | 4224          | 48   |                  |
| LOP_MODIFY_ROW        | LCX_PFS            | Unknown Alloc Unit        | 13632         | 169  | 13632         | 169  |                  |
| LOP_MODIFY_ROW        | LCX_TEXT_MIX       | Unknown Alloc Unit        | 108640        | 120  | 108640        | 120  |                  |
| LOP_ROOT_CHANGE       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 960           | 10   | 960           | 10   |                  |
| LOP_SET_BITS          | LCX_GAM            | Unknown Alloc Unit        | 1200          | 20   | 1200          | 20   |                  |
| LOP_SET_BITS          | LCX_IAM            | Unknown Alloc Unit        | 1080          | 18   | 1080          | 18   |                  |
| LOP_SET_BITS          | LCX_SGAM           | Unknown Alloc Unit        | 120           | 2    | 120           | 2    |                  |
| LOP_SHRINK_NOOP       | LCX_NULL           |                           |               |      | 32            | 1    | 32               |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Total                 |                    |                           | 410144        | 1095 | 411232        | 1092 | 1088             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
Martin Smith
fonte
11
+1 Apenas por curiosidade (e para ser um pouco pedante). A questão é / era bastante antiga (agosto de 2008), então era sobre o SQL 2005. Agora estamos em 2011 (final de) e o SQL mais recente é o 2008 R2 mais o beta do Denali. Qual versão você usou?
Xanatos
2
@xanatos - 2008. Em 2005, as variáveis ​​de tabela estariam realmente em desvantagem, pois INSERT ... SELECTnão eram minimamente registradas e você não pode SELECT INTO ... uma variável de tabela.
Martin Smith
11
Obrigado @MartinSmith, atualizei minha resposta para remover a reivindicação sobre o log.
Rory
18

Em quais cenários um supera o outro?

Para tabelas menores (menos de 1000 linhas), use uma variável temp, caso contrário, use uma tabela temp.

SQLMenace
fonte
17
Algum dado de suporte? Isso não é muito útil apenas como uma afirmação por si só.
Michael Myers
8
A Microsoft recomenda o limite de 100 linhas: msdn.microsoft.com/en-us/library/ms175010.aspx (consulte a seção Práticas recomendadas).
Artemix 14/03
Veja minha resposta abaixo para uma explicação.
Weihui Guo 16/04
17

@wcm - na verdade, para escolher nit a variável de tabela não é apenas ram - ela pode ser parcialmente armazenada no disco.

Uma tabela temporária pode ter índices, enquanto uma variável da tabela pode ter apenas um índice primário. Se a velocidade é um problema As variáveis ​​da tabela podem ser mais rápidas, mas obviamente se houver muitos registros ou a necessidade de pesquisar a tabela temporária de um índice em cluster, uma tabela temporária seria melhor.

Bom artigo de base

JamesSugrue
fonte
2
Bom artigo de base +1. Excluirei minha resposta, pois a modificação não deixaria muito e já existem muitas boas respostas.
wcm
12
  1. Tabela Temp: Uma tabela Temp é fácil de criar e fazer backup de dados.

    Variável de tabela: Mas a variável de tabela envolve o esforço quando geralmente criamos as tabelas normais.

  2. Tabela temporária: o resultado da tabela temporária pode ser usado por vários usuários.

    Variável de tabela: Mas a variável de tabela pode ser usada apenas pelo usuário atual. 

  3. Tabela temporária: a tabela temporária será armazenada no tempdb. Isso criará tráfego de rede. Quando temos dados grandes na tabela temporária, ele precisa trabalhar no banco de dados. Existe um problema de desempenho.

    Variável de tabela: mas uma variável de tabela será armazenada na memória física de alguns dados e, posteriormente, quando o tamanho aumentar, será movida para o tempdb.

  4. Tabela Temp: a tabela Temp pode executar todas as operações DDL. Permite criar os índices, soltar, alterar, etc.,

    Variável de tabela: enquanto a variável de tabela não permitirá realizar operações DDL. Mas a variável da tabela nos permite criar apenas o índice clusterizado.

  5. Tabela temporária: a tabela temporária pode ser usada para a sessão atual ou global. Para que uma sessão com vários usuários possa utilizar os resultados na tabela.

    Variável de tabela: Mas a variável de tabela pode ser usada até esse programa. (Procedimento armazenado)

  6. Tabela Temp: a variável Temp não pode usar as transações. Quando fazemos as operações DML com a tabela temp, ela pode ser revertida ou confirmar as transações.

    Variável de tabela: Mas não podemos fazer isso para a variável de tabela.

  7. Tabela Temp: As funções não podem usar a variável temp. Mais ainda, não podemos executar a operação DML nas funções.

    Variável de tabela: Mas a função nos permite usar a variável de tabela. Mas, usando a variável de tabela, podemos fazer isso.

  8. Tabela Temp: O procedimento armazenado fará a recompilação (não é possível usar o mesmo plano de execução) quando usamos a variável temp para todas as chamadas subseqüentes.

    Variável de tabela: Considerando que a variável de tabela não será assim.

Kumar Manish
fonte
8

Para todos vocês que acreditam no mito de que variáveis ​​temporárias estão apenas na memória

Primeiro, a variável da tabela NÃO é necessariamente residente na memória. Sob pressão de memória, as páginas pertencentes a uma variável da tabela podem ser enviadas para tempdb.

Leia o artigo aqui: TempDB :: variável de tabela vs tabela temporária local

SQLMenace
fonte
3
Você pode editar suas respostas em uma única resposta abordando os dois pontos?
Joshua Drake
7

A outra principal diferença é que as variáveis ​​de tabela não possuem estatísticas de coluna, como nas tabelas temporárias. Isso significa que o otimizador de consulta não sabe quantas linhas existem na variável da tabela (ele supõe 1), o que pode levar à geração de planos altamente não ideais se a variável da tabela realmente tiver um grande número de linhas.

Monstro de Gila
fonte
2
A rowscoluna in sys.partitionsé mantida para as variáveis ​​da tabela, para que ele realmente saiba quantas linhas existem na tabela. Isso pode ser visto usando OPTION (RECOMPILE). Mas a falta de estatísticas da coluna significa que não é possível estimar predicados específicos da coluna.
Martin Smith
7

Citação retirada; Solução de problemas e internos profissionais do SQL Server 2012

Estatísticas A principal diferença entre tabelas temporárias e variáveis ​​de tabela é que as estatísticas não são criadas nas variáveis ​​da tabela. Isso tem duas conseqüências principais, a primeira das quais é que o Query Optimizer usa uma estimativa fixa para o número de linhas em uma variável de tabela, independentemente dos dados que ele contém. Além disso, adicionar ou remover dados não altera a estimativa.

Índices Você não pode criar índices nas variáveis ​​da tabela, embora possa criar restrições. Isso significa que, ao criar chaves primárias ou restrições exclusivas, você pode ter índices (como eles são criados para suportar restrições) nas variáveis ​​da tabela. Mesmo se você tiver restrições e, portanto, índices que terão estatísticas, os índices não serão usados ​​quando a consulta for compilada, porque não existirão no tempo de compilação nem causarão recompilações.

Modificações de esquema As modificações de esquema são possíveis em tabelas temporárias, mas não em variáveis ​​de tabela. Embora modificações de esquema sejam possíveis em tabelas temporárias, evite usá-las porque elas causam recompilações de instruções que usam as tabelas.

Tabelas temporárias versus variáveis ​​de tabela

VARIÁVEIS DE MESA NÃO SÃO CRIADAS NA MEMÓRIA

Existe um equívoco comum de que as variáveis ​​de tabela são estruturas na memória e, como tal, terão desempenho mais rápido que as tabelas temporárias . Graças a um DMV chamado sys. dm _ db _ session _ space _ use, que mostra o uso do tempdb por sessão, você pode provar que não é esse o caso . Após reiniciar o SQL Server para limpar a DMV, execute o seguinte script para confirmar que o id da sua sessão retorna 0 para o usuário _ objetos _ alocar _ página _ contagem:

SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Agora você pode verificar quanto espaço uma tabela temporária usa executando o script a seguir para criar uma tabela temporária com uma coluna e preenchê-la com uma linha:

CREATE TABLE #TempTable ( ID INT ) ;
INSERT INTO #TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Os resultados no meu servidor indicam que a tabela foi alocada uma página no tempdb. Agora execute o mesmo script, mas use uma variável de tabela desta vez:

DECLARE @TempTable TABLE ( ID INT ) ;
INSERT INTO @TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Qual usar?

O uso ou não de tabelas temporárias ou variáveis ​​de tabela deve ser decidido por testes detalhados, mas é melhor se inclinar para tabelas temporárias como padrão, porque há muito menos coisas que podem dar errado .

Vi clientes desenvolver código usando variáveis ​​de tabela porque estavam lidando com uma pequena quantidade de linhas e era mais rápido que uma tabela temporária, mas alguns anos depois havia centenas de milhares de linhas na variável de tabela e o desempenho foi terrível. , portanto, tente permitir um planejamento de capacidade ao tomar sua decisão!

Teoman shipahi
fonte
De fato, as estatísticas são criadas nas variáveis ​​da tabela, consulte stackoverflow.com/questions/42824366/…
YuFeng Shen 16/17
4

Outra diferença:

Uma tabela var pode ser acessada apenas a partir de instruções no procedimento que a cria, e não de outros procedimentos chamados por esse procedimento ou SQL dinâmico aninhado (via exec ou sp_executesql).

O escopo de uma tabela temporária, por outro lado, inclui código nos procedimentos chamados e SQL dinâmico aninhado.

Se a tabela criada por seu procedimento precisar ser acessível a partir de outros procedimentos chamados ou SQL dinâmico, você deverá usar uma tabela temporária. Isso pode ser muito útil em situações complexas.

BrianFinkel
fonte
2

As diferenças entre Temporary Tables (##temp/#temp)e Table Variables (@table)são as seguintes:

  1. Table variable (@table)é criado no memory. Considerando que, a Temporary table (##temp/#temp)é criado no tempdb database. No entanto, se houver uma pressão de memória, as páginas pertencentes a uma variável da tabela podem ser enviadas para tempdb.

  2. Table variablesnão pode estar envolvido transactions, logging or locking. Isso faz @table faster then #temp. Portanto, a variável de tabela é mais rápida que a tabela temporária.

  3. Temporary tablepermite modificações de esquema diferentes Table variables.

  4. Temporary tablessão visíveis na rotina criada e também nas rotinas filho. Considerando que, variáveis ​​de tabela são visíveis apenas na rotina criada.

  5. Temporary tablessão permitidos CREATE INDEXesenquanto, Table variablesnão são permitidos CREATE INDEXem vez disso, pode ter índice usando Primary Key or Unique Constraint.

Litisqe Kumar
fonte
1

Considere também que você pode frequentemente substituir as duas por tabelas derivadas, que também podem ser mais rápidas. Porém, como em todos os ajustes de desempenho, apenas testes reais em relação aos dados reais podem indicar a melhor abordagem para sua consulta específica.

HLGEM
fonte
1

Surpreende-me que ninguém tenha mencionado a principal diferença entre esses dois é que a tabela temporária suporta inserção paralela, enquanto a variável da tabela não. Você poderá ver a diferença no plano de execução. E aqui está o vídeo dos Workshops SQL no Canal 9 .

Isso também explica por que você deve usar uma variável temp para tabelas menores, caso contrário, use uma tabela temp, como SQLMenace respondeu anteriormente.

Weihui Guo
fonte