Dit spørgsmål viser, at du er bukket under for nogle af de almindelige misforståelser omkring tabelvariabler og midlertidige tabeller.
Jeg har skrevet et ret omfattende svar på DBA's side, hvor jeg ser på forskellene mellem de to objekttyper. Dette adresserer også dit spørgsmål om disk vs hukommelse (jeg så ikke nogen væsentlig forskel i adfærd mellem de to).
Med hensyn til spørgsmålet i titlen om hvornår man skal bruge en tabelvariabel kontra en lokal midlertidig tabel, har man dog ikke altid et valg. I funktioner er det for eksempel kun muligt at bruge en tabelvariabel, og hvis du skal skrive til tabellen i et child scope, så er det kun en #temp
tabel vil gøre (tabel-værdi parametre tillader skrivebeskyttet adgang).
Hvor du har et valg, er nogle forslag nedenfor (selvom den mest pålidelige metode er blot at teste begge med din specifikke arbejdsbyrde).
-
Hvis du har brug for et indeks, der ikke kan oprettes på en tabelvariabel, skal du naturligvis bruge en
#temporary
bord. Detaljerne i dette er dog versionsafhængige. For SQL Server 2012 og derunder var de eneste indekser, der kunne oprettes på tabelvariabler dem, der var implicit oprettet gennem enUNIQUE
ellerPRIMARY KEY
begrænsning. SQL Server 2014 introducerede indbygget indekssyntaks for en undergruppe af de tilgængelige muligheder iCREATE INDEX
. Dette er blevet forlænget siden for at tillade filtrerede indeksforhold. Indekser medINCLUDE
-d-kolonner eller columnstore-indekser er dog stadig ikke mulige at oprette på tabelvariabler. -
Hvis du gentagne gange vil tilføje og slette et stort antal rækker fra tabellen, skal du bruge en
#temporary
bord. Det understøtterTRUNCATE
(hvilket er mere effektivt endDELETE
for store tabeller) og yderligere efterfølgende indsættelser efter enTRUNCATE
kan have bedre ydeevne end dem efter enDELETE
som illustreret her. - Hvis du vil slette eller opdatere et stort antal rækker, kan den midlertidige tabel muligvis fungere meget bedre end en tabelvariabel - hvis den er i stand til at bruge rækkesætdeling (se "Effekter af rækkesætdeling" nedenfor for et eksempel) .
- Hvis den optimale plan ved hjælp af tabellen vil variere afhængigt af data, så brug en
#temporary
bord. Det understøtter oprettelse af statistikker, som gør det muligt for planen at blive dynamisk rekompileret i overensstemmelse med dataene (selvom for cachelagrede midlertidige tabeller i lagrede procedurer skal rekompileringsadfærden forstås separat). - Hvis det er usandsynligt, at den optimale plan for forespørgslen ved hjælp af tabellen nogensinde ændrer sig, kan du overveje en tabelvariabel for at springe over overheaden ved oprettelse af statistik og genkompilering (vil muligvis kræve tip til at rette den plan, du ønsker).
- Hvis kilden til de data, der er indsat i tabellen, er fra en potentielt dyr
SELECT
sætning, så overvej, at brug af en tabelvariabel vil blokere muligheden for dette ved at bruge en parallel plan. - Hvis du har brug for dataene i tabellen for at overleve en tilbagerulning af en ydre brugertransaktion, så brug en tabelvariabel. En mulig use case for dette kan være at logge forløbet af forskellige trin i en lang SQL-batch.
- Når du bruger en
#temp
tabel i en bruger transaktionslåse kan holdes længere end for tabelvariabler (potentielt indtil slutningen af transaktionen vs slutningen af sætningen afhængig af typen af lås og isolationsniveau), og det kan også forhindre trunkering aftempdb
transaktionslog indtil brugertransaktionen slutter. Så dette kan begunstige brugen af tabelvariabler. - Inden for lagrede rutiner kan både tabelvariabler og midlertidige tabeller cachelagres. Metadatavedligeholdelsen for cachelagrede tabelvariabler er mindre end for
#temporary
tabeller. Bob Ward påpeger i sintempdb
præsentation af, at dette kan forårsage yderligere uenighed på systemtabeller under forhold med høj samtidighed. Når der er tale om små mængder data, kan dette desuden gøre en målbar forskel for ydeevnen.
Effekter af rækkesætdeling
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