Din fråga visar att du har fallit för några av de vanliga missuppfattningarna kring tabellvariabler och tillfälliga tabeller.
Jag har skrivit ett ganska omfattande svar på DBA-webbplatsen och tittat på skillnaderna mellan de två objekttyperna. Detta tar också upp din fråga om disk vs minne (jag såg ingen signifikant skillnad i beteende mellan de två).
När det gäller frågan i rubriken om när man ska använda en tabellvariabel kontra en lokal temporär tabell har man inte alltid ett val. I funktioner är det till exempel endast möjligt att använda en tabellvariabel och om du behöver skriva till tabellen i ett underordnat scope så är det bara en #temp
tabell kommer att göra (tabellvärdade parametrar tillåter skrivskyddad åtkomst).
Där du har ett val finns några förslag nedan (även om den mest tillförlitliga metoden är att helt enkelt testa båda med din specifika arbetsbelastning).
-
Om du behöver ett index som inte kan skapas på en tabellvariabel så behöver du naturligtvis en
#temporary
tabell. Detaljerna i detta är dock versionsberoende. För SQL Server 2012 och lägre var de enda index som kunde skapas på tabellvariabler de som implicit skapades genom enUNIQUE
ellerPRIMARY KEY
begränsning. SQL Server 2014 introducerade inline indexsyntax för en delmängd av alternativen som är tillgängliga iCREATE INDEX
. Detta har förlängts sedan för att tillåta filtrerade indexvillkor. Index medINCLUDE
-d-kolumner eller columnstore-index är dock fortfarande inte möjliga att skapa på tabellvariabler. -
Om du upprepade gånger kommer att lägga till och ta bort ett stort antal rader från tabellen, använd en
#temporary
tabell. Som stöderTRUNCATE
(vilket är mer effektivt änDELETE
för stora tabeller) och dessutom efterföljande infogning efter enTRUNCATE
kan ha bättre prestanda än de som följer enDELETE
som illustreras här. - Om du kommer att ta bort eller uppdatera ett stort antal rader kan temptabellen fungera mycket bättre än en tabellvariabel - om den kan använda raduppsättningsdelning (se "Effekter av raduppsättningsdelning" nedan för ett exempel) .
- Om den optimala planen som använder tabellen kommer att variera beroende på data, använd en
#temporary
tabell. Det stöder skapandet av statistik som gör att planen kan kompileras dynamiskt enligt data (men för cachade temporära tabeller i lagrade procedurer måste omkompileringsbeteendet förstås separat). - Om det är osannolikt att den optimala planen för frågan som använder tabellen någonsin kommer att ändras, kan du överväga en tabellvariabel för att hoppa över omkostnader för statistikskapande och omkompilering (skulle eventuellt kräva tips för att fixa planen du vill ha).
- Om källan för data som infogas i tabellen kommer från en potentiellt dyr
SELECT
tänk sedan på att användning av en tabellvariabel kommer att blockera möjligheten för detta med en parallell plan. - Om du behöver data i tabellen för att överleva en återställning av en extern användartransaktion, använd en tabellvariabel. Ett möjligt användningsfall för detta kan vara att logga förloppet för olika steg i en lång SQL-batch.
- När du använder en
#temp
tabell inom en användares transaktionslås kan hållas längre än för tabellvariabler (potentiellt till slutet av transaktionen kontra slutet av satsen beroende på typen av lås och isoleringsnivå) och det kan också förhindra trunkering avtempdb
transaktionslogg tills användartransaktionen slutar. Så detta kan gynna användningen av tabellvariabler. - Inom lagrade rutiner kan både tabellvariabler och temporära tabeller cachelagras. Metadataunderhållet för cachade tabellvariabler är mindre än det för
#temporary
tabeller. Bob Ward påpekar i sintempdb
presentation att detta kan orsaka ytterligare konflikter på systemtabeller under förhållanden med hög samtidighet. Dessutom, när man hanterar små mängder data kan detta göra en mätbar skillnad för prestandan.
Effekter av raduppsättningsdelning
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