Questa sembra essere un'area con parecchi miti e visioni contrastanti.
Quindi qual è la differenza tra una variabile di tabella e una tabella temporanea locale in SQL Server?
Questa sembra essere un'area con parecchi miti e visioni contrastanti.
Quindi qual è la differenza tra una variabile di tabella e una tabella temporanea locale in SQL Server?
Risposte:
Contenuto
Avvertimento
Questa risposta discute le variabili di tabella "classiche" introdotte in SQL Server 2000. SQL Server 2014 in memoria OLTP introduce tipi di tabelle ottimizzate per la memoria. Le istanze variabili di tabella di queste sono diverse per molti aspetti rispetto a quelle discusse di seguito! ( maggiori dettagli ).
Posizione di archiviazione
Nessuna differenza. Entrambi sono memorizzati in tempdb
.
Ho visto suggerire che per le variabili di tabella questo non è sempre il caso, ma questo può essere verificato dal basso
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
Risultati di esempio (che mostra la posizione nelle tempdb
2 righe sono memorizzati)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
Posizione logica
@table_variables
comportarsi più come se facessero parte del database corrente rispetto alle #temp
tabelle. Per le variabili di tabella (dal 2005) le regole di confronto delle colonne, se non specificate in modo esplicito, saranno quelle del database corrente, mentre per le #temp
tabelle utilizzerà le regole di confronto predefinite di tempdb
( Maggiori dettagli ). Inoltre, i tipi di dati definiti dall'utente e le raccolte XML devono essere in tempdb da utilizzare per le #temp
tabelle, ma le variabili di tabella possono utilizzarle dal database corrente ( Origine ).
SQL Server 2012 introduce database contenuti. il comportamento delle tabelle temporanee in questi differisce (h / t Aaron)
In un database contenuto i dati della tabella temporanea vengono raccolti nelle regole di confronto del database contenuto.
- Tutti i metadati associati alle tabelle temporanee (ad esempio nomi di tabelle e colonne, indici e così via) saranno inclusi nelle regole di confronto del catalogo.
- I vincoli con nome non possono essere utilizzati nelle tabelle temporanee.
- Le tabelle temporanee non possono fare riferimento a tipi definiti dall'utente, raccolte di schemi XML o funzioni definite dall'utente.
Visibilità a diversi ambiti
@table_variables
è possibile accedere solo all'interno del batch e nell'ambito in cui sono dichiarati. #temp_tables
sono accessibili all'interno di batch secondari (trigger nidificati, procedura, exec
chiamate). #temp_tables
creato nell'ambito esterno ( @@NESTLEVEL=0
) può estendersi anche ai batch poiché persistono fino al termine della sessione. Nessuno dei due tipi di oggetto può essere creato in un batch figlio e accessibile nell'ambito di chiamata, tuttavia, come discusso in seguito (è possibile tuttavia utilizzare le##temp
tabelle globali ).
Tutta la vita
@table_variables
vengono creati implicitamente quando DECLARE @.. TABLE
viene eseguito un batch contenente un'istruzione (prima che venga eseguito qualsiasi codice utente in quel batch) e vengono rilasciati implicitamente alla fine.
Sebbene il parser non ti consenta di provare a utilizzare la variabile di tabella prima DECLARE
dell'istruzione, la creazione implicita può essere vista sotto.
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tables
vengono creati in modo esplicito quando CREATE TABLE
viene rilevata l' istruzione TSQL e possono essere rilasciati in modo esplicito con DROP TABLE
o verranno eliminati in modo implicito quando il batch termina (se creato in un batch figlio con @@NESTLEVEL > 0
) o quando la sessione termina in altro modo.
NB: All'interno delle routine memorizzate è possibile memorizzare nella cache entrambi i tipi di oggetto anziché creare e rilasciare ripetutamente nuove tabelle. Ci sono restrizioni su quando può verificarsi questa memorizzazione nella cache, tuttavia che è possibile violare #temp_tables
ma che le restrizioni su @table_variables
impediscono comunque. Il sovraccarico di manutenzione per le #temp
tabelle memorizzate nella cache è leggermente maggiore rispetto alle variabili della tabella, come illustrato qui .
Metadati oggetto
Questo è essenzialmente lo stesso per entrambi i tipi di oggetto. È memorizzato nelle tabelle di base del sistema in tempdb
. È più semplice vedere una #temp
tabella, tuttavia, poiché OBJECT_ID('tempdb..#T')
può essere utilizzata per digitare le tabelle di sistema e il nome generato internamente è più strettamente correlato al nome definito CREATE TABLE
nell'istruzione. Per le variabili di tabella la object_id
funzione non funziona e il nome interno è interamente generato dal sistema senza alcuna relazione con il nome della variabile. Quanto segue dimostra che i metadati sono ancora presenti digitando un nome di colonna (si spera univoco). Per le tabelle senza nomi di colonna univoci, object_id può essere determinato usando DBCC PAGE
purché non siano vuoti.
/*Declare a table variable with some unusual options.*/
DECLARE @T TABLE
(
[dba.se] INT IDENTITY PRIMARY KEY NONCLUSTERED,
A INT CHECK (A > 0),
B INT DEFAULT 1,
InRowFiller char(1000) DEFAULT REPLICATE('A',1000),
OffRowFiller varchar(8000) DEFAULT REPLICATE('B',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast('C' as varchar(max)),10000),
UNIQUE CLUSTERED (A,B)
WITH (FILLFACTOR = 80,
IGNORE_DUP_KEY = ON,
DATA_COMPRESSION = PAGE,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS=ON)
)
INSERT INTO @T (A)
VALUES (1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13)
SELECT t.object_id,
t.name,
p.rows,
a.type_desc,
a.total_pages,
a.used_pages,
a.data_pages,
p.data_compression_desc
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.tables AS t
ON t.object_id = p.object_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se'
Produzione
Duplicate key was ignored.
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| object_id | name | rows | type_desc | total_pages | used_pages | data_pages | data_compression_desc |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | PAGE |
| 574625090 | #22401542 | 13 | LOB_DATA | 24 | 19 | 0 | PAGE |
| 574625090 | #22401542 | 13 | ROW_OVERFLOW_DATA | 16 | 14 | 0 | PAGE |
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | NONE |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
Le transazioni
Le operazioni su @table_variables
vengono eseguite come transazioni di sistema, indipendentemente da qualsiasi transazione dell'utente esterno, mentre le #temp
operazioni della tabella equivalente verrebbero eseguite come parte della transazione dell'utente stesso. Per questo motivo un ROLLBACK
comando influenzerà una #temp
tabella ma lascerà @table_variable
intatto.
DECLARE @T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO @T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM @T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM @T
DROP TABLE #T
Registrazione
Entrambi generano record di registro nel registro delle tempdb
transazioni. Un malinteso comune è che questo non è il caso delle variabili di tabella, quindi uno script che dimostra questo è di seguito, dichiara una variabile di tabella, aggiunge un paio di righe, quindi le aggiorna e le elimina.
Poiché la variabile della tabella viene creata e rilasciata in modo implicito all'inizio e alla fine del batch, è necessario utilizzare più batch per visualizzare la registrazione completa.
USE tempdb;
/*
Don't run this on a busy server.
Ideally should be no concurrent activity at all
*/
CHECKPOINT;
GO
/*
The 2nd column is binary to allow easier correlation with log output shown later*/
DECLARE @T TABLE ([C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3] INT, B BINARY(10))
INSERT INTO @T
VALUES (1, 0x41414141414141414141),
(2, 0x41414141414141414141)
UPDATE @T
SET B = 0x42424242424242424242
DELETE FROM @T
/*Put allocation_unit_id into CONTEXT_INFO to access in next batch*/
DECLARE @allocId BIGINT, @Context_Info VARBINARY(128)
SELECT @Context_Info = allocation_unit_id,
@allocId = a.allocation_unit_id
FROM sys.system_internals_allocation_units a
INNER JOIN sys.partitions p
ON p.hobt_id = a.container_id
INNER JOIN sys.columns c
ON c.object_id = p.object_id
WHERE ( c.name = 'C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3' )
SET CONTEXT_INFO @Context_Info
/*Check log for records related to modifications of table variable itself*/
SELECT Operation,
Context,
AllocUnitName,
[RowLog Contents 0],
[Log Record Length]
FROM fn_dblog(NULL, NULL)
WHERE AllocUnitId = @allocId
GO
/*Check total log usage including updates against system tables*/
DECLARE @allocId BIGINT = CAST(CONTEXT_INFO() AS BINARY(8));
WITH T
AS (SELECT Operation,
Context,
CASE
WHEN AllocUnitId = @allocId THEN 'Table Variable'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Base Table'
ELSE AllocUnitName
END AS AllocUnitName,
[Log Record Length]
FROM fn_dblog(NULL, NULL) AS D)
SELECT Operation = CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
AllocUnitName,
[Size in Bytes] = COALESCE(SUM([Log Record Length]), 0),
Cnt = COUNT(*)
FROM T
GROUP BY GROUPING SETS( ( Operation, Context, AllocUnitName ), ( ) )
ORDER BY GROUPING(Operation),
AllocUnitName
ritorna
Per quanto sono stato in grado di discernere le operazioni su entrambi generano quantità approssimativamente uguali di registrazione.
Mentre la quantità di registrazione è molto simile, una differenza importante è che i record di registro relativi alle #temp
tabelle non possono essere cancellati fino a quando una transazione dell'utente contenente non termina così una transazione di lunga durata che a un certo punto scrive sulle #temp
tabelle impedirà il troncamento del registro tempdb
mentre le transazioni autonome generato per le variabili di tabella no.
Le variabili di tabella non supportano TRUNCATE
quindi possono essere in svantaggio di registrazione quando il requisito è rimuovere tutte le righe da una tabella (anche se per tabelle molto piccole DELETE
può comunque funzionare meglio )
Cardinalità
Molti dei piani di esecuzione che coinvolgono variabili di tabella mostreranno una singola riga stimata come output da essi. L'ispezione delle proprietà della variabile di tabella mostra che SQL Server ritiene che la variabile di tabella abbia zero righe (Perché stima che una riga verrà emessa da una tabella di zero righe è spiegata da @Paul White qui ).
Tuttavia, i risultati mostrati nella sezione precedente mostrano un rows
conteggio accurato in sys.partitions
. Il problema è che nella maggior parte dei casi le istruzioni che fanno riferimento alle variabili della tabella vengono compilate mentre la tabella è vuota. Se l'istruzione viene (ri) compilata dopo che @table_variable
è stata compilata, questa verrà invece utilizzata per la cardinalità della tabella (ciò potrebbe accadere a causa di un esplicito recompile
o forse perché l'istruzione fa riferimento anche a un altro oggetto che provoca una compilazione posticipata o una ricompilazione).
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES(1),(2),(3),(4),(5)
CREATE TABLE #T(I INT)
/*Reference to #T means this statement is subject to deferred compile*/
SELECT * FROM @T WHERE NOT EXISTS(SELECT * FROM #T)
DROP TABLE #T
Il piano mostra un conteggio di righe stimato accurato dopo la compilazione differita.
In SQL Server 2012 SP2, è stato introdotto il flag di traccia 2453. Maggiori dettagli sono disponibili in "Motore relazionale" qui .
Quando questo flag di tracciamento è abilitato, è possibile che i ricompilamenti automatici tengano conto della cardinalità modificata, come discusso ulteriormente a breve.
NB: In Azure nel livello di compatibilità 150 la compilazione dell'istruzione è ora rinviata alla prima esecuzione . Ciò significa che non sarà più soggetto al problema di stima della riga zero.
Nessuna statistica di colonna
Avere una cardinalità della tabella più accurata non significa tuttavia che il conteggio delle righe stimato sarà più accurato (a meno che non si esegua un'operazione su tutte le righe della tabella). SQL Server non mantiene affatto le statistiche di colonna per le variabili di tabella, quindi ricadrà su ipotesi basate sul predicato del confronto (ad esempio, il 10% della tabella verrà restituito per una =
colonna non univoca o il 30% per un >
confronto). Al contrario, le statistiche delle colonne vengono mantenute per le #temp
tabelle.
SQL Server mantiene un conteggio del numero di modifiche apportate a ciascuna colonna. Se il numero di modifiche da quando il piano è stato compilato supera la soglia di ricompilazione (RT), il piano verrà ricompilato e le statistiche aggiornate. RT dipende dal tipo e dalle dimensioni della tabella.
Dal piano di memorizzazione nella cache in SQL Server 2008
RT è calcolato come segue. (n si riferisce alla cardinalità di una tabella quando viene compilato un piano di query.)
Tabella permanente
- Se n <= 500, RT = 500.
- Se n> 500, RT = 500 + 0,20 * n.Tabella temporanea
- Se n <6, RT = 6.
- Se 6 <= n <= 500, RT = 500.
- Se n> 500, RT = 500 + 0,20 * n.
Variabile di tabella
- RT non esiste. Pertanto, non si verificano ricompilazioni a causa di cambiamenti nelle cardinalità delle variabili di tabella. (Ma vedi nota su TF 2453 di seguito)
il KEEP PLAN
suggerimento può essere usato per impostare RT per le #temp
tabelle come per le tabelle permanenti.
L'effetto netto di tutto ciò è che spesso i piani di esecuzione generati per le #temp
tabelle sono ordini di grandezza migliori rispetto a @table_variables
quando sono coinvolte molte righe poiché SQL Server ha informazioni migliori con cui lavorare.
NB1: Le variabili di tabella non dispongono di statistiche ma possono comunque presentare un evento di ricompilazione "Statistiche modificate" sotto il flag di traccia 2453 (non si applica ai piani "banali") Ciò sembra verificarsi sotto le stesse soglie di ricompilazione mostrate per le tabelle temporanee sopra con un uno aggiuntivo che se N=0 -> RT = 1
. cioè tutte le istruzioni compilate quando la variabile della tabella è vuota finiranno per ottenere una ricompilazione e corrette TableCardinality
la prima volta che vengono eseguite quando non sono vuote. La cardinalità della tabella dei tempi di compilazione viene archiviata nel piano e se l'istruzione viene eseguita nuovamente con la stessa cardinalità (a causa del flusso di istruzioni di controllo o del riutilizzo di un piano memorizzato nella cache) non si verifica alcuna ricompilazione.
NB2: per le tabelle temporanee memorizzate nella cache nelle stored procedure, la storia della ricompilazione è molto più complicata di quanto sopra descritto. Vedere le tabelle temporanee nelle procedure memorizzate per tutti i dettagli gory.
ricompilazioni
Oltre alle ricompilazioni basate sulla modifica sopra descritte, le #temp
tabelle possono anche essere associate a compilazioni aggiuntive semplicemente perché consentono operazioni vietate per le variabili di tabella che attivano una compilazione (ad esempio modifiche DDL CREATE INDEX
, ALTER TABLE
)
Blocco
È stato affermato che le variabili di tabella non partecipano al blocco. Questo non è il caso. Eseguendo gli output seguenti nella scheda Messaggi SSMS, i dettagli dei blocchi acquisiti e rilasciati per un'istruzione insert.
DECLARE @tv_target TABLE (c11 int, c22 char(100))
DBCC TRACEON(1200,-1,3604)
INSERT INTO @tv_target (c11, c22)
VALUES (1, REPLICATE('A',100)), (2, REPLICATE('A',100))
DBCC TRACEOFF(1200,-1,3604)
Per le query che SELECT
dalle variabili della tabella Paul White sottolinea nei commenti che queste vengono automaticamente fornite con un NOLOCK
suggerimento implicito . Questo è mostrato sotto
DECLARE @T TABLE(X INT);
SELECT X
FROM @T
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)
*** Output Tree: (trivial plan) ***
PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002 Hints( NOLOCK )
L'impatto di questo sul blocco potrebbe tuttavia essere di lieve entità.
SET NOCOUNT ON;
CREATE TABLE #T( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @T TABLE ( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @I INT = 0
WHILE (@I < 10000)
BEGIN
INSERT INTO #T DEFAULT VALUES
INSERT INTO @T DEFAULT VALUES
SET @I += 1
END
/*Run once so compilation output doesn't appear in lock output*/
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEON(1200,3604,-1)
SELECT *, sys.fn_PhysLocFormatter(%%physloc%%)
FROM @T
PRINT '--*--'
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEOFF(1200,3604,-1)
DROP TABLE #T
Nessuno di questi risultati restituisce un ordine di chiave indice che indica che SQL Server ha utilizzato una scansione ordinata per allocazione per entrambi.
Ho eseguito lo script sopra due volte e i risultati per la seconda esecuzione sono riportati di seguito
Process 58 acquiring Sch-S lock on OBJECT: 2:-1325894110:0 (class bit0 ref1) result: OK
--*--
Process 58 acquiring IS lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 acquiring S lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 releasing lock on OBJECT: 2:-1293893996:0
L'output del blocco per la variabile della tabella è davvero estremamente minimo poiché SQL Server acquisisce solo un blocco della stabilità dello schema sull'oggetto. Ma per un #temp
tavolo è quasi altrettanto leggero in quanto elimina un S
blocco a livello di oggetto . Un NOLOCK
suggerimento o READ UNCOMMITTED
livello di isolamento può naturalmente essere specificato in modo esplicito quando si lavora con #temp
le tabelle pure.
Analogamente al problema con la registrazione di una transazione dell'utente circostante, è possibile che i blocchi vengano mantenuti più a lungo per le #temp
tabelle. Con lo script qui sotto
--BEGIN TRAN;
CREATE TABLE #T (X INT,Y CHAR(4000) NULL);
INSERT INTO #T (X) VALUES(1)
SELECT CASE resource_type
WHEN 'OBJECT' THEN OBJECT_NAME(resource_associated_entity_id, 2)
WHEN 'ALLOCATION_UNIT' THEN (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.allocation_units a
JOIN tempdb.sys.partitions p ON a.container_id = p.hobt_id
WHERE a.allocation_unit_id = resource_associated_entity_id)
WHEN 'DATABASE' THEN DB_NAME(resource_database_id)
ELSE (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.partitions
WHERE partition_id = resource_associated_entity_id)
END AS object_name,
*
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
DROP TABLE #T
-- ROLLBACK
quando eseguito al di fuori di una transazione utente esplicita per entrambi i casi, l'unico blocco restituito durante il controllo sys.dm_tran_locks
è un blocco condiviso su DATABASE
.
Quando si rimuove il commento, le BEGIN TRAN ... ROLLBACK
26 righe vengono restituite mostrando che i blocchi vengono mantenuti sia sull'oggetto stesso che sulle righe della tabella di sistema per consentire il rollback e impedire ad altre transazioni di leggere dati non impegnati. L'operazione di variabile tabella equivalente non è soggetta a rollback con la transazione dell'utente e non è necessario conservare questi blocchi per consentirci di verificare nell'istruzione successiva, ma i blocchi di traccia acquisiti e rilasciati in Profiler o utilizzando il flag di traccia 1200 mostrano che molti eventi di blocco continuano a si verificano.
indici
Per le versioni precedenti a SQL Server 2014 gli indici possono essere creati in modo implicito solo sulle variabili di tabella come effetto collaterale dell'aggiunta di un vincolo univoco o chiave primaria. Ciò significa ovviamente che sono supportati solo indici univoci. Un indice non cluster non univoco su una tabella con un indice cluster univoco può essere simulato tuttavia semplicemente dichiarandolo UNIQUE NONCLUSTERED
e aggiungendo la chiave CI alla fine della chiave NCI desiderata (SQL Server lo farebbe comunque dietro le quinte anche se un non univoco NCI potrebbe essere specificato)
Come dimostrato in precedenza, index_option
è possibile specificare vari s nella dichiarazione del vincolo tra cui DATA_COMPRESSION
, IGNORE_DUP_KEY
e FILLFACTOR
(anche se non ha senso impostarne uno in quanto farebbe solo una differenza sulla ricostruzione dell'indice e non è possibile ricostruire gli indici sulle variabili della tabella!)
Inoltre, le variabili di tabella non supportano INCLUDE
colonne d, indici filtrati (fino al 2016) o partizionamento, mentre le #temp
tabelle (lo schema di partizione deve essere creato in tempdb
).
Indici in SQL Server 2014
Gli indici non univoci possono essere dichiarati in linea nella definizione della variabile di tabella in SQL Server 2014. Di seguito è riportata una sintassi di esempio.
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);
Indici in SQL Server 2016
Da CTP 3.1 è ora possibile dichiarare indici filtrati per variabili di tabella. Con RTM può accadere che anche le colonne incluse siano consentite, anche se probabilmente non lo trasformeranno in SQL16 a causa di vincoli di risorse
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
Parallelismo
Le query che vengono inserite (o modificate in altro modo) @table_variables
non possono avere un piano parallelo, #temp_tables
non sono limitate in questo modo.
C'è una soluzione apparente in quella riscrittura, poiché la SELECT
parte seguente permette che la parte si svolga in parallelo ma che finisce usando una tabella temporanea nascosta (dietro le quinte)
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
Non vi è tale limitazione nelle query che selezionano dalle variabili di tabella come illustrato nella mia risposta qui
Altre differenze funzionali
#temp_tables
non può essere utilizzato all'interno di una funzione. @table_variables
può essere utilizzato all'interno di UDF scalari o multiistruzione.@table_variables
non può avere vincoli con nome.@table_variables
non può essere SELECT
-ed INTO
, ALTER
-ed, TRUNCATE
d o essere il bersaglio di DBCC
comandi come DBCC CHECKIDENT
o di SET IDENTITY INSERT
e non supporta suggerimenti di tabella comeWITH (FORCESCAN)
CHECK
i vincoli sulle variabili di tabella non vengono considerati dall'ottimizzatore per semplificazione, predicati impliciti o rilevamento di contraddizioni.PAGELATCH_EX
attese. ( Esempio )Solo memoria?
Come affermato all'inizio, entrambi vengono archiviati nelle pagine in tempdb
. Tuttavia, non ho indicato se ci fossero differenze nel comportamento quando si tratta di scrivere queste pagine su disco.
Ho fatto una piccola quantità di test su questo ora e finora non ho visto alcuna differenza. Nel test specifico che ho fatto sulla mia istanza di SQL Server 250 pagine sembra essere il punto di interruzione prima che il file di dati venga scritto.
NB: il comportamento riportato di seguito non si verifica più in SQL Server 2014 o SQL Server 2012 SP1 / CU10 o SP2 / CU1 il writer desideroso non è più desideroso di scrivere pagine su disco. Maggiori dettagli su tale modifica in SQL Server 2014: tempdb Hidden Performance Gem .
Esecuzione dello script seguente
CREATE TABLE #T(X INT, Filler char(8000) NULL)
INSERT INTO #T(X)
SELECT TOP 250 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values
DROP TABLE #T
E il monitoraggio scrive sul tempdb
file di dati con Process Monitor non ne ho visto nessuno (tranne quelli occasionali nella pagina di avvio del database con offset 73.728). Dopo essere passato 250
a 251
ho iniziato a vedere le scritture come di seguito.
Lo screenshot sopra mostra le scritture di pagine 5 * 32 e una singola pagina che indica che 161 delle pagine sono state scritte su disco. Ho avuto lo stesso punto di interruzione di 250 pagine durante il test anche con le variabili di tabella. Lo script seguente mostra un modo diverso di guardaresys.dm_os_buffer_descriptors
DECLARE @T TABLE (
X INT,
[dba.se] CHAR(8000) NULL)
INSERT INTO @T
(X)
SELECT TOP 251 Row_number() OVER (ORDER BY (SELECT 0))
FROM master..spt_values
SELECT is_modified,
Count(*) AS page_count
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = (SELECT a.allocation_unit_id
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se')
GROUP BY is_modified
is_modified page_count
----------- -----------
0 192
1 61
Mostra che 192 pagine sono state scritte su disco e la bandiera sporca cancellata. Mostra anche che la scrittura su disco non significa che le pagine verranno immediatamente eliminate dal pool di buffer. Le query su questa variabile di tabella potrebbero essere soddisfatte interamente dalla memoria.
Su un server inattivo con pagine di pool di buffer max server memory
impostate 2000 MB
e di DBCC MEMORYSTATUS
report allocate come circa 1.843.000 KB (circa 23.000 pagine) ho inserito le tabelle sopra in lotti di 1.000 righe / pagine e per ogni iterazione registrata.
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
Sia la variabile della #temp
tabella che la tabella hanno dato grafici quasi identici e sono riusciti a massimizzare il pool di buffer prima di arrivare al punto in cui non erano interamente conservati in memoria, quindi non sembra esserci alcuna limitazione particolare su quanta memoria entrambi possono consumare.
Ci sono alcune cose che vorrei sottolineare sulla base di esperienze particolari piuttosto che di studio. Come DBA, sono molto nuovo, quindi per favore correggimi dove richiesto.