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
#temporarybord. 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 enUNIQUEellerPRIMARY KEYbegræ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
#temporarybord. Det understøtterTRUNCATE(hvilket er mere effektivt endDELETEfor store tabeller) og yderligere efterfølgende indsættelser efter enTRUNCATEkan have bedre ydeevne end dem efter enDELETEsom 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
#temporarybord. 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
SELECTsæ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
#temptabel 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 aftempdbtransaktionslog 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
#temporarytabeller. Bob Ward påpeger i sintempdbpræ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