Qual è la differenza tra una tabella temporanea e una variabile di tabella in SQL Server?


390

In SQL Server 2005, possiamo creare tabelle temporanee in due modi:

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

o

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

Quali sono le differenze tra questi due? Ho letto opinioni contrastanti sul fatto che @tmp usi ancora tempdb o se tutto accade in memoria.

In quali scenari uno supera le prestazioni dell'altro?



2
C'è davvero un ottimo articolo scritto da Pinal Dave qui ... blog.sqlauthority.com/2009/12/15/…
sam yi,

Risposte:


392

Ci sono alcune differenze tra le tabelle temporanee (#tmp) e le variabili di tabella (@tmp), sebbene l'utilizzo di tempdb non sia uno di questi, come spiegato nel link MSDN di seguito.

Come regola generale, per volumi di dati medio-piccoli e semplici scenari di utilizzo è necessario utilizzare le variabili di tabella. (Questa è una linea guida troppo ampia con ovviamente molte eccezioni - vedi sotto e articoli seguenti.)

Alcuni punti da considerare quando si sceglie tra di loro:

  • Le tabelle temporanee sono tabelle reali in modo da poter fare cose come CREARE INDICI, ecc. Se si dispone di grandi quantità di dati per i quali l'accesso per indice sarà più veloce, le tabelle temporanee sono una buona opzione.

  • Le variabili di tabella possono avere indici utilizzando i vincoli PRIMARY KEY o UNIQUE. (Se si desidera un indice non univoco, includere solo la colonna della chiave primaria come ultima colonna nel vincolo univoco. Se non si dispone di una colonna univoca, è possibile utilizzare una colonna identità.) SQL 2014 ha anche indici non univoci .

  • Le variabili di tabella non partecipano alle transazioni e SELECTsono implicitamente con NOLOCK. Il comportamento della transazione può essere molto utile, ad esempio se si desidera eseguire il ROLLBACK durante una procedura, le variabili di tabella popolate durante quella transazione saranno comunque popolate!

  • Le tabelle temporanee potrebbero comportare la ricompilazione di stored procedure, forse spesso. Le variabili della tabella no.

  • È possibile creare una tabella temporanea utilizzando SELECT INTO, che può essere più veloce da scrivere (ottimo per l'interrogazione ad hoc) e può consentire di gestire la modifica dei tipi di dati nel tempo, poiché non è necessario definire la struttura della tabella temporanea in anticipo.

  • È possibile restituire le variabili della tabella dalle funzioni, consentendo di incapsulare e riutilizzare la logica molto più facilmente (ad esempio, rendere una funzione per dividere una stringa in una tabella di valori su un delimitatore arbitrario).

  • L'uso delle variabili di tabella all'interno delle funzioni definite dall'utente consente di utilizzare tali funzioni in modo più ampio (vedere la documentazione CREATE FUNCTION per i dettagli). Se stai scrivendo una funzione, dovresti usare le variabili di tabella rispetto alle tabelle temporanee, a meno che non ci sia un'esigenza convincente altrimenti.

  • Sia le variabili di tabella che le tabelle temporanee sono archiviate in tempdb. Ma le variabili di tabella (dal 2005) impostano automaticamente le regole di confronto del database corrente rispetto alle tabelle temporanee che accettano le regole di confronto predefinite di tempdb ( ref ). Ciò significa che è necessario essere consapevoli dei problemi di confronto se si utilizzano tabelle temporanee e le regole di confronto db sono diverse da quelle di tempdb, causando problemi se si desidera confrontare i dati nella tabella temporanea con i dati nel database.

  • Le tabelle temporanee globali (## tmp) sono un altro tipo di tabelle temporanee disponibili per tutte le sessioni e gli utenti.

Qualche ulteriore lettura:


26
Le variabili di tabella possono avere indici. Basta creare un vincolo univoco e si ottiene automaticamente un indice. Fa un'enorme differenza di prestazioni. (Se non si desidera un indice univoco, è sufficiente aggiungere la chiave primaria effettiva alla fine dei campi desiderati. Se non ne si possiede uno, creare una colonna di identità).
Ben

7
@Ben And SQL Server 2014 consente di specificare
Martin Smith

4
Le variabili di tabella non interessate dalle transazioni sono utili a volte. Se hai qualcosa che vuoi conservare dopo un rollback, puoi inserirlo in una variabile di tabella.
Quillbreaker,

3
Le statistiche vengono create per le tabelle temporanee, che possono migliorare i piani di query, ma non per le variabili di tabella. Queste statistiche vengono memorizzate nella cache per un po ', insieme alle pagine della tabella temporanea, dopo che la tabella temporanea viene eliminata e potrebbe non essere precisa se la tabella memorizzata nella cache viene riattivata.
Michael Green,

Le variabili di tabella verranno impostate automaticamente sul confronto del tipo di dati definito dall'utente (se la colonna è di un tipo di dati definito dall'utente) o sul confronto del database corrente e non sul confronto predefinito del tempdb. Le tabelle temporanee utilizzeranno le regole di confronto predefinite tempdb. Vedi: technet.microsoft.com/en-us/library/ms188927.aspx
PseudoToad,

25

Solo guardando l'affermazione nella risposta accettata che le variabili di tabella non partecipano alla registrazione.

Sembra generalmente falso che ci sia qualche differenza nella quantità di registrazione (almeno per insert/ update/ deleteoperazioni sulla tabella stessa anche se da allora ho scoperto che c'è qualche piccola differenza in questo senso per gli oggetti temporanei memorizzati nella cache nelle procedure memorizzate a causa della tabella di sistema aggiuntiva gli aggiornamenti).

Ho esaminato il comportamento di registrazione rispetto a una @table_variablee una #temptabella per le seguenti operazioni.

  1. Inserimento riuscito
  2. Inserimento multi-riga in cui l'istruzione è stata ripristinata a causa della violazione del vincolo.
  3. Aggiornare
  4. Elimina
  5. Dealloca

I record del registro delle transazioni erano quasi identici per tutte le operazioni.

La versione della variabile della tabella ha in realtà alcune voci di registro extra perché ottiene una voce aggiunta (e successivamente rimossa) dalla sys.syssingleobjrefstabella di base, ma nel complesso aveva pochi byte in meno registrati puramente poiché il nome interno per le variabili di tabella consuma 236 byte in meno rispetto alle #temptabelle (118 nvarcharcaratteri in meno ).

Script completo da riprodurre (esecuzione ottimale su un'istanza avviata in modalità utente singolo e sqlcmdmodalità utilizzo )

: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),())

risultati

+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
|                       |                    |                           |             @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             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+

1
+1 Solo per curiosità (e per essere un po 'pedante). La domanda è / era piuttosto vecchia (agosto 2008), quindi si trattava di SQL 2005. Ora siamo nel 2011 (fine del) e l'ultimo SQL è il 2008 R2 più la beta Denali. Quale versione hai usato?
xanatos,

2
@xanatos - 2008. Nel 2005 le variabili di tabella sarebbero effettivamente svantaggiate poiché INSERT ... SELECTnon sono state minimamente registrate e non è possibile utilizzare SELECT INTO ... una variabile di tabella.
Martin Smith,

1
Grazie @MartinSmith, ho aggiornato la mia risposta per rimuovere il reclamo sulla registrazione.
Rory,

18

In quali scenari uno supera le prestazioni dell'altro?

Per tabelle più piccole (meno di 1000 righe) utilizzare una variabile temp, altrimenti utilizzare una tabella temporanea.


17
Qualche dato di supporto? Questo non è molto utile solo come affermazione da solo.
Michael Myers

8
Microsoft consiglia un limite di 100 righe: msdn.microsoft.com/en-us/library/ms175010.aspx (vedere la sezione Best practice).
Artemix,

Vedi la mia risposta qui sotto per una spiegazione.
Weihui Guo,

17

@wcm - in realtà scegliere la variabile Table non è solo Ram - può essere parzialmente memorizzato sul disco.

Una tabella temporanea può avere indici, mentre una variabile di tabella può avere solo un indice primario. Se la velocità è un problema Le variabili della tabella possono essere più veloci, ma ovviamente se ci sono molti record o la necessità di cercare la tabella temporanea di un indice cluster, una tabella temporanea sarebbe migliore.

Buon articolo di fondo


2
Buono articolo di base +1. Cancellerò la mia risposta poiché la modifica non lascerebbe molto e ci sono già così tante buone risposte
wcm

12
  1. Tabella temporanea: una tabella temporanea è semplice per creare e eseguire il backup dei dati.

    Variabile di tabella: ma la variabile di tabella comporta lo sforzo quando di solito creiamo le tabelle normali.

  2. Tabella temporanea: il risultato della tabella temporanea può essere utilizzato da più utenti.

    Variabile di tabella: ma la variabile di tabella può essere utilizzata solo dall'utente corrente. 

  3. Tabella temporanea: la tabella temporanea verrà memorizzata nel tempdb. Farà traffico di rete. Quando abbiamo dati di grandi dimensioni nella tabella temporanea, allora deve funzionare attraverso il database. Esisterà un problema di prestazioni.

    Variabile di tabella: ma una variabile di tabella verrà archiviata nella memoria fisica per alcuni dei dati, quindi in seguito quando la dimensione aumenta verrà spostata nel tempdb.

  4. Tabella temporanea: la tabella temporanea può eseguire tutte le operazioni DDL. Permette di creare indici, rilasciarli, alterarli, ecc.,

    Variabile di tabella: considerando che la variabile di tabella non consente di eseguire le operazioni DDL. Ma la variabile table ci consente di creare solo l'indice cluster.

  5. Tabella temporanea: la tabella temporanea può essere utilizzata per la sessione corrente o globale. In modo che una sessione con più utenti possa utilizzare i risultati nella tabella.

    Variabile di tabella: ma la variabile di tabella può essere utilizzata fino a quel programma. (Procedura memorizzata)

  6. Tabella temporanea: la variabile temporanea non può utilizzare le transazioni. Quando eseguiamo le operazioni DML con la tabella temporanea, è possibile eseguire il rollback o eseguire il commit delle transazioni.

    Variabile di tabella: ma non possiamo farlo per la variabile di tabella.

  7. Tabella temporanea: le funzioni non possono utilizzare la variabile temporanea. Inoltre non possiamo eseguire l'operazione DML nelle funzioni.

    Variabile di tabella: ma la funzione ci consente di utilizzare la variabile di tabella. Ma usando la variabile table possiamo farlo.

  8. Tabella temporanea: la procedura memorizzata eseguirà la ricompilazione (non è possibile utilizzare lo stesso piano di esecuzione) quando utilizziamo la variabile temporanea per ogni chiamata successiva.

    Variabile di tabella: considerando che la variabile di tabella non farà così.


8

Per tutti voi che credete al mito che le variabili temporanee siano solo nella memoria

Innanzitutto, la variabile di tabella NON è necessariamente residente in memoria. Sotto pressione della memoria, le pagine appartenenti a una variabile di tabella possono essere inviate a tempdb.

Leggi l'articolo qui: TempDB :: Variabile di tabella vs tabella temporanea locale


3
Puoi modificare le tue risposte in un'unica risposta indirizzando i due punti?
Joshua Drake,

7

L'altra differenza principale è che le variabili di tabella non hanno statistiche di colonna, come fanno le tabelle temporanee. Ciò significa che Query Optimizer non sa quante righe ci sono nella variabile della tabella (suppone 1), il che può portare a piani altamente non ottimali generati se la variabile della tabella ha effettivamente un gran numero di righe.


2
La rowscolonna in sys.partitionsè mantenuta per le variabili della tabella, quindi in realtà sa quante righe ci sono nella tabella. Questo può essere visto usando OPTION (RECOMPILE). Ma la mancanza di statistiche di colonna significa che non è possibile stimare predicati di colonna specifici.
Martin Smith,

7

Citazione tratta da; Interni professionali di SQL Server 2012 e risoluzione dei problemi

Statistiche La principale differenza tra le tabelle temporanee e le variabili di tabella è che le statistiche non vengono create sulle variabili di tabella. Ciò ha due conseguenze principali, la prima delle quali è che lo Strumento per ottimizzare le query utilizza una stima fissata per il numero di righe in una variabile di tabella indipendentemente dai dati in essa contenuti. Inoltre, l'aggiunta o la rimozione di dati non modifica la stima.

Indici Non è possibile creare indici su variabili di tabella sebbene sia possibile creare vincoli. Ciò significa che creando chiavi primarie o vincoli univoci, è possibile disporre di indici (in quanto creati per supportare i vincoli) sulle variabili della tabella. Anche se si hanno vincoli, e quindi indici che avranno statistiche, gli indici non verranno utilizzati quando la query viene compilata perché non esisteranno al momento della compilazione, né causeranno ricompilazioni.

Modifiche dello schema Le modifiche allo schema sono possibili su tabelle temporanee ma non su variabili di tabella. Sebbene le modifiche allo schema siano possibili su tabelle temporanee, evitare di usarle perché causano ricompilazioni di istruzioni che utilizzano le tabelle.

Tabelle temporanee contro variabili di tabella

VARIABILI DELLA TABELLA NON SONO CREATI IN MEMORIA

C'è un malinteso comune sul fatto che le variabili di tabella siano strutture in memoria e come tali funzioneranno più rapidamente delle tabelle temporanee . Grazie a un DMV chiamato sys. dm _ db _ sessione _ spazio _ utilizzo, che mostra l'utilizzo di tempdb per sessione, puoi provare che non è così . Dopo aver riavviato SQL Server per cancellare DMV, eseguire il seguente script per confermare che la sessione _ id restituisce 0 per l'utente _ oggetti _ alloc _ pagina _ conteggio:

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

Ora puoi controllare quanto spazio utilizza una tabella temporanea eseguendo il seguente script per creare una tabella temporanea con una colonna e popolarla con una riga:

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 ;

I risultati sul mio server indicano che alla tabella è stata allocata una pagina in tempdb. Ora esegui lo stesso script ma questa volta usa una variabile di tabella:

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 ;

Quale usare?

L'utilizzo o meno di tabelle temporanee o variabili di tabella deve essere deciso mediante test approfonditi, ma è preferibile orientarsi verso le tabelle temporanee come impostazione predefinita perché ci sono molte meno cose che possono andare storte .

Ho visto i clienti sviluppare codice usando le variabili della tabella perché avevano a che fare con una piccola quantità di righe, ed era più veloce di una tabella temporanea, ma qualche anno dopo c'erano centinaia di migliaia di righe nella variabile della tabella e le prestazioni erano terribili , quindi prova a consentire una pianificazione della capacità quando prendi la tua decisione!


In statistica infatti sono creati su variabili di tabella, vedere stackoverflow.com/questions/42824366/...
Yufeng Shen

4

Un'altra differenza:

Una tabella var è accessibile solo dalle istruzioni all'interno della procedura che la crea, non da altre procedure chiamate da quella procedura o da SQL dinamico nidificato (tramite exec o sp_executesql).

L'ambito di una tabella temporanea, d'altra parte, include il codice nelle procedure chiamate e nell'SQL dinamico nidificato.

Se la tabella creata dalla procedura deve essere accessibile da altre procedure chiamate o SQL dinamico, è necessario utilizzare una tabella temporanea. Questo può essere molto utile in situazioni complesse.


2

Le differenze tra Temporary Tables (##temp/#temp)e Table Variables (@table)sono come:

  1. Table variable (@table)viene creato in memory. Considerando che a Temporary table (##temp/#temp)viene creato in tempdb database. Tuttavia, se c'è una pressione della memoria, le pagine appartenenti a una variabile di tabella possono essere inviate a tempdb.

  2. Table variablesnon può essere coinvolto in transactions, logging or locking. Questo fa @table faster then #temp. Quindi la tabella variabile è più veloce della tabella temporanea.

  3. Temporary tableconsente le modifiche dello schema diversamente Table variables.

  4. Temporary tablessono visibili nella routine creata e anche nelle routine figlio. Considerando che, le variabili di tabella sono visibili solo nella routine creata.

  5. Temporary tablessono consentiti CREATE INDEXesmentre, Table variablesnon sono consentiti CREATE INDEXinvece possono avere un indice usando Primary Key or Unique Constraint.


1

Considera anche che puoi spesso sostituire entrambi con tabelle derivate che potrebbero essere anche più veloci. Come in tutte le ottimizzazioni delle prestazioni, tuttavia, solo i test effettivi sui dati effettivi possono indicare l'approccio migliore per la query specifica.


1

Mi sorprende che nessuno abbia menzionato la differenza chiave tra questi due è che la tabella temporanea supporta l' inserimento parallelo mentre la variabile tabella no. Dovresti essere in grado di vedere la differenza dal piano di esecuzione. Ed ecco il video di SQL Workshops su Channel 9 .

Questo spiega anche perché dovresti usare una variabile temporanea per tabelle più piccole, altrimenti usa una tabella temporanea, come aveva già risposto SQLMenace .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.