Quando dovrei usare una tabella variabile vs tabella temporanea nel server SQL?


298

Sto imparando maggiori dettagli nella tabella variabile. Dice che le tabelle temporanee sono sempre su disco e che le variabili della tabella sono in memoria, vale a dire che le prestazioni della variabile tabella sono migliori della tabella temporanea poiché la variabile tabella utilizza meno operazioni di I / O della tabella temporanea.

Ma a volte, se ci sono troppi record in una variabile di tabella che non possono essere contenuti in memoria, la variabile di tabella verrà inserita sul disco come la tabella temporanea.

Ma non so cosa siano i "troppi dischi". 100.000 record? o 1000.000 record? Come posso sapere se una variabile di tabella che sto usando è in memoria o è su disco? Esiste una funzione o uno strumento in SQL Server 2005 per misurare la scala della variabile della tabella o per farmi sapere quando la variabile della tabella viene messa sul disco dalla memoria?


5
Una variabile di tabella è quasi sempre presente tempDB: "in memoria" è un mito. Inoltre: le variabili di tabella verranno sempre considerate dall'utilità di ottimizzazione delle query per contenere esattamente una riga: se ne hai molte di più, questo può portare a piani di esecuzione seriamente sbagliati.
marc_s


2
@marc_s - Puoi rilasciare il "quasi" in quell'affermazione. È sempre in tempdb(ma potrebbe anche essere interamente in memoria)
Martin Smith l'

2
Con SQL 2014 è ora possibile creare una variabile di tabella in memoria
paparazzo,

Risposte:


362

La tua domanda mostra che hai ceduto ad alcune delle idee sbagliate comuni che circondano le variabili di tabella e le tabelle temporanee.

Ho scritto una risposta abbastanza ampia sul sito DBA esaminando le differenze tra i due tipi di oggetto. Questo risolve anche la tua domanda sul disco rispetto alla memoria (non ho riscontrato alcuna differenza significativa nel comportamento tra i due).

Per quanto riguarda la domanda nel titolo su quando utilizzare una variabile di tabella rispetto a una tabella temporanea locale, non è sempre possibile scegliere. Nelle funzioni, ad esempio, è possibile utilizzare solo una variabile di tabella e se è necessario scrivere sulla tabella in un ambito figlio, #templo farà solo una tabella (i parametri con valori di tabella consentono l' accesso in sola lettura ).

Di seguito sono riportati alcuni suggerimenti (sebbene il metodo più affidabile sia semplicemente testare entrambi con il carico di lavoro specifico).

  1. Se hai bisogno di un indice che non può essere creato su una variabile di tabella, avrai ovviamente bisogno di una #temporarytabella. I dettagli di questo dipendono comunque dalla versione. Per SQL Server 2012 e inferiori gli unici indici che potevano essere creati su variabili di tabella erano quelli implicitamente creati attraverso un vincolo UNIQUEo PRIMARY KEY. SQL Server 2014 ha introdotto la sintassi dell'indice inline per un sottoinsieme delle opzioni disponibili in CREATE INDEX. Questo è stato esteso da allora per consentire condizioni di indice filtrate. INCLUDETuttavia, non è ancora possibile creare indici con variabili -d o indici columnstore.

  2. Se aggiungerai ed eliminerai ripetutamente un gran numero di righe dalla tabella, usa una #temporarytabella. Ciò supporta TRUNCATE(che è più efficiente rispetto DELETEalle tabelle di grandi dimensioni) e gli inserimenti successivi successivi a TRUNCATEpossono avere prestazioni migliori rispetto a quelli che seguono DELETE come illustrato qui .

  3. Se si eliminerà o aggiornerà un numero elevato di righe, la tabella temporanea potrebbe avere prestazioni molto migliori rispetto a una variabile di tabella, se è in grado di utilizzare la condivisione del set di righe (vedere "Effetti della condivisione del set di righe" di seguito per un esempio).
  4. Se il piano ottimale che utilizza la tabella varia in base ai dati, utilizzare una #temporarytabella. Ciò supporta la creazione di statistiche che consentono di ricompilare dinamicamente il piano in base ai dati (sebbene per le tabelle temporanee memorizzate nella cache nelle procedure memorizzate il comportamento della ricompilazione debba essere compreso separatamente).
  5. Se è improbabile che il piano ottimale per la query che utilizza la tabella cambi, è possibile prendere in considerazione una variabile di tabella per saltare l'overhead della creazione e della ricompilazione delle statistiche (potrebbe essere necessario un suggerimento per correggere il piano desiderato).
  6. Se l'origine per i dati inseriti nella tabella proviene da un potenzialmente costoso SELECT un'istruzione , considerare che l'utilizzo di una variabile di tabella bloccherà la possibilità di utilizzare un piano parallelo.
  7. Se sono necessari i dati nella tabella per sopravvivere al rollback di una transazione dell'utente esterno, utilizzare una variabile di tabella. Un possibile caso d'uso per questo potrebbe essere la registrazione dell'avanzamento di diversi passaggi in un lungo batch SQL.
  8. Quando si utilizza una #temptabella all'interno di un utente, i blocchi delle transazioni possono essere conservati più a lungo rispetto alle variabili di tabella (potenzialmente fino alla fine della transazione rispetto alla fine dell'istruzione in base al tipo di blocco e al livello di isolamento) e può anche impedire il troncamento del tempdbregistro delle transazioni fino al la transazione dell'utente termina. Quindi questo potrebbe favorire l'uso delle variabili di tabella.
  9. All'interno delle routine memorizzate, è possibile memorizzare nella cache sia variabili di tabella che tabelle temporanee. La manutenzione dei metadati per le variabili delle tabelle memorizzate nella cache è inferiore a quella delle #temporarytabelle. Bob Ward sottolinea nella sua tempdbpresentazione che ciò può causare ulteriore contesa sulle tabelle di sistema in condizioni di elevata concorrenza. Inoltre, quando si ha a che fare con piccole quantità di dati, ciò può fare una differenza misurabile in termini di prestazioni .

Effetti della condivisione del set di righe

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

2
Salve, signor Martin Smith. In questo caso, voglio solo archiviare un set di valori ID per usarli in altre query all'interno della procedura Store. Allora cosa mi consigliate?
Jeancarlo Fontalvo,

@JeancarloFontalvo - una variabile di tabella con una chiave primaria attivata ide l'uso di OPTION (RECOMPILE)probabilmente andrebbe bene per questo - ma prova entrambi.
Martin Smith,

la contesa sui metadati è uguale sia per la tabella temporanea che per la variabile tabella?
Syed Aqeel Ashiq

@Syed. Generalmente meno per la TV. I blocchi possono essere rilasciati prima se all'interno di una transazione utente. Vedi anche il link Bob Ward.
Martin Smith,

73

Utilizzare una variabile di tabella se per una quantità molto piccola di dati (migliaia di byte)

Utilizzare una tabella temporanea per molti dati

Un altro modo di pensarci: se pensi di poter beneficiare di un indice, di statistiche automatizzate o di qualsiasi bontà dell'ottimizzatore SQL, il tuo set di dati è probabilmente troppo grande per una variabile di tabella.

Nel mio esempio, volevo solo mettere circa 20 righe in un formato e modificarle come gruppo, prima di usarle per AGGIORNARE / INSERIRE una tabella permanente. Quindi una variabile da tavolo è perfetta.

Ma sto anche eseguendo SQL per il back-fill di migliaia di righe alla volta, e posso sicuramente affermare che le tabelle temporanee funzionano molto meglio delle variabili di tabella.

Questo non è diverso dal modo in cui i CTE sono un problema per un motivo di dimensioni simili - se i dati nel CTE sono molto piccoli, trovo che un CTE funzioni altrettanto bene o meglio di quello che viene fornito dall'ottimizzatore, ma se è abbastanza grande allora ti fa male.

La mia comprensione si basa principalmente su http://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/ , che ha molti più dettagli.


La variabile da asporto è una tabella che va bene per piccoli set di dati, ma usa una tabella temporanea per un set di dati più grande. Ho una query con migliaia di righe. Passando dalla tabella variabile alla tabella temporanea, il tempo di query scende da 40 a soli 5 secondi con tutto il resto uguale.
liang

42

Microsoft dice qui

Le variabili di tabella non hanno statistiche di distribuzione, non attiveranno ricompilazioni. Pertanto, in molti casi, l'ottimizzatore costruirà un piano di query presupponendo che la variabile della tabella non abbia righe. Per questo motivo, si dovrebbe essere cauti sull'uso di una variabile di tabella se si prevede un numero maggiore di righe (maggiore di 100). Le tabelle temporanee possono essere una soluzione migliore in questo caso.


14

Sono totalmente d'accordo con Abacus (scusate, non ho abbastanza punti per commentare).

Inoltre, tieni presente che non si riduce necessariamente a quanti record hai, ma alle dimensioni dei tuoi record.

Ad esempio, hai considerato la differenza di prestazioni tra 1.000 record con 50 colonne ciascuno contro 100.000 record con solo 5 colonne ciascuno?

Infine, forse stai interrogando / archiviando più dati del necessario? Ecco una buona lettura delle strategie di ottimizzazione SQL . Limita la quantità di dati che stai estraendo, soprattutto se non li usi tutti (alcuni programmatori SQL diventano pigri e selezionano semplicemente tutto anche se usano solo un piccolo sottoinsieme). Non dimenticare che l'analizzatore di query SQL potrebbe anche diventare il tuo migliore amico.


4

La tabella delle variabili è disponibile solo per la sessione corrente, ad esempio, se è necessaria EXECun'altra procedura memorizzata all'interno di quella corrente, sarà necessario passare la tabella poiché, Table Valued Parameterovviamente, ciò influirà sulle prestazioni, con le tabelle temporanee è possibile farlo solo con passando il nome della tabella temporanea

Per testare una tabella temporanea:

  • Aprire l'editor di query di Management Studio
  • Crea una tabella temporanea
  • Apri un'altra finestra dell'editor di query
  • Seleziona da questa tabella "Disponibile"

Per testare una tabella delle variabili:

  • Aprire l'editor di query di Management Studio
  • Crea una tabella delle variabili
  • Apri un'altra finestra dell'editor di query
  • Seleziona da questa tabella "Non disponibile"

qualcos'altro che ho sperimentato è: se il tuo schema non ha il GRANTprivilegio di creare tabelle, usa le tabelle variabili.


3

scrivendo i dati nelle tabelle dichiarate declare @tbe dopo essermi unito ad altre tabelle, mi sono reso conto che il tempo di risposta rispetto alle tabelle temporanee tempdb .. # tbè molto più alto.

Quando mi unisco a loro con @tb il tempo è molto più lungo per restituire il risultato, a differenza di #tm , il ritorno è quasi istantaneo.

Ho fatto dei test con un join di 10.000 righe e unisco con altri 5 tavoli


Potresti pubblicare il test che hai eseguito per ottenere queste cifre?
Dan Def,
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.