Uw vraag laat zien dat u bent bezweken voor enkele van de veelvoorkomende misvattingen over tabelvariabelen en tijdelijke tabellen.
Ik heb een vrij uitgebreid antwoord geschreven op de DBA-site en kijk naar de verschillen tussen de twee objecttypen. Dit beantwoordt ook uw vraag over schijf versus geheugen (ik zag geen significant verschil in gedrag tussen de twee).
Wat betreft de vraag in de titel over wanneer je een tabelvariabele versus een lokale tijdelijke tabel moet gebruiken, heb je niet altijd een keuze. In functies is het bijvoorbeeld alleen mogelijk om een tabelvariabele te gebruiken en als u naar de tabel in een onderliggende scope moet schrijven, dan alleen een #temp
tabel zal doen (parameter met tabelwaarde staat alleen-lezen toegang toe).
Waar u wel een keuze heeft, vindt u hieronder enkele suggesties (hoewel de meest betrouwbare methode is om beide te testen met uw specifieke werklast).
-
Als je een index nodig hebt die niet kan worden gemaakt op een tabelvariabele, dan heb je natuurlijk een
#temporary
nodig tafel. De details hiervan zijn echter versie-afhankelijk. Voor SQL Server 2012 en lager waren de enige indexen die op tabelvariabelen konden worden gemaakt, de indexen die impliciet waren gemaakt via eenUNIEKE
ofPRIMAIRE SLEUTEL
beperking. SQL Server 2014 introduceerde inline indexsyntaxis voor een subset van de opties die beschikbaar zijn inCREATE INDEX
. Dit is sindsdien uitgebreid om gefilterde indexvoorwaarden mogelijk te maken. Indexen metINCLUDE
-d kolommen of columnstore-indexen kunnen echter nog steeds niet worden gemaakt op tabelvariabelen. -
Als u herhaaldelijk grote aantallen rijen aan de tabel toevoegt en verwijdert, gebruik dan een
#tijdelijk
tafel. Dat ondersteuntTRUNCATE
(wat efficiënter is danDELETE
voor grote tabellen) en aanvullende invoegingen na eenTRUNCATE
kunnen betere prestaties leveren dan die na eenDELETE
zoals hier geïllustreerd. - Als u een groot aantal rijen gaat verwijderen of bijwerken, presteert de tijdelijke tabel mogelijk veel beter dan een tabelvariabele - als deze het delen van rijensets kan gebruiken (zie "Effecten van het delen van rijen" hieronder voor een voorbeeld) .
- Als het optimale plan voor het gebruik van de tabel afhankelijk is van de gegevens, gebruik dan een
#tijdelijk
tafel. Dat ondersteunt het maken van statistieken waarmee het plan dynamisch opnieuw kan worden gecompileerd op basis van de gegevens (hoewel voor tijdelijke tabellen in de cache in opgeslagen procedures het hercompilatiegedrag afzonderlijk moet worden begrepen). - Als het onwaarschijnlijk is dat het optimale plan voor de query met behulp van de tabel ooit zal veranderen, kunt u een tabelvariabele overwegen om de overhead van het maken van statistieken en het opnieuw compileren over te slaan (u hebt mogelijk hints nodig om het gewenste plan te repareren).
- Als de bron voor de gegevens die in de tabel zijn ingevoegd afkomstig is van een mogelijk dure
SELECT
verklaring, bedenk dan dat het gebruik van een tabelvariabele de mogelijkheid hiervan met een parallel plan zal blokkeren. - Als je de gegevens in de tabel nodig hebt om een terugdraaiing van een externe gebruikerstransactie te overleven, gebruik dan een tabelvariabele. Een mogelijke use case hiervoor is het loggen van de voortgang van verschillende stappen in een lange SQL-batch.
- Bij gebruik van een
#temp
tabel binnen een gebruiker transactievergrendelingen kunnen langer worden vastgehouden dan voor tabelvariabelen (mogelijk tot het einde van de transactie versus het einde van de verklaring, afhankelijk van het type vergrendeling en isolatieniveau) en het kan ook voorkomen dat detempdb
transactielogboek totdat de gebruikerstransactie eindigt. Dit zou dus het gebruik van tabelvariabelen kunnen bevorderen. - Binnen opgeslagen routines kunnen zowel tabelvariabelen als tijdelijke tabellen in de cache worden opgeslagen. Het onderhoud van metagegevens voor tabelvariabelen in de cache is minder dan voor
#tijdelijk
tafels. Bob Ward wijst erop in zijntempdb
presentatie dat dit bij hoge gelijktijdigheid kan leiden tot extra twist op systeemtabellen. Bovendien kan dit bij het omgaan met kleine hoeveelheden gegevens een meetbaar verschil maken voor de prestaties.
Effecten van het delen van rijen
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