Ihre Frage zeigt, dass Sie einigen der häufigsten Missverständnisse in Bezug auf Tabellenvariablen und temporäre Tabellen erlegen sind.
Ich habe auf der DBA-Site eine ziemlich ausführliche Antwort geschrieben, die sich mit den Unterschieden zwischen den beiden Objekttypen befasst. Hiermit wird auch Ihre Frage zu Festplatte und Speicher beantwortet (ich habe keinen signifikanten Unterschied im Verhalten zwischen den beiden festgestellt).
In Bezug auf die Frage im Titel, wann eine Tabellenvariable im Vergleich zu einer lokalen temporären Tabelle verwendet werden soll, haben Sie nicht immer die Wahl. In Funktionen ist es beispielsweise nur möglich, eine Tabellenvariable zu verwenden. Wenn Sie in einem untergeordneten Bereich in die Tabelle schreiben müssen, reicht nur eine #temp
Tabelle aus (tabellenwertige Parameter ermöglichen schreibgeschützten Zugriff ).
Wenn Sie die Wahl haben, finden Sie unten einige Vorschläge (obwohl die zuverlässigste Methode darin besteht, beide einfach mit Ihrer spezifischen Arbeitslast zu testen).
Wenn Sie einen Index benötigen, der nicht für eine Tabellenvariable erstellt werden kann, benötigen Sie natürlich eine #temporary
Tabelle. Die Details hierzu sind jedoch versionabhängig. Für SQL Server 2012 und darunter waren die einzigen Indizes, die für Tabellenvariablen erstellt werden konnten, diejenigen, die implizit durch eine UNIQUE
oder PRIMARY KEY
-Einschränkung erstellt wurden. In SQL Server 2014 wurde die Inline-Indexsyntax für eine Teilmenge der in verfügbaren Optionen eingeführt CREATE INDEX
. Dies wurde seitdem erweitert, um gefilterte Indexbedingungen zu ermöglichen. Indizes mit INCLUDE
-d Spalten oder Columnstore-Indizes können jedoch immer noch nicht für Tabellenvariablen erstellt werden.
Wenn Sie wiederholt eine große Anzahl von Zeilen zur Tabelle hinzufügen und daraus löschen, verwenden Sie eine #temporary
Tabelle. Dies unterstützt TRUNCATE
(was effizienter ist als DELETE
bei großen Tabellen) und zusätzlich nachfolgende Einfügungen nach a TRUNCATE
können eine bessere Leistung haben als solche nach a, DELETE
wie hier dargestellt .
- Wenn Sie eine große Anzahl von Zeilen löschen oder aktualisieren, ist die temporäre Tabelle möglicherweise wesentlich leistungsfähiger als eine Tabellenvariable - wenn sie die Rowset-Freigabe verwenden kann (ein Beispiel finden Sie unten unter "Auswirkungen der Rowset-Freigabe").
- Wenn der optimale Plan unter Verwendung der Tabelle abhängig von den Daten variiert, verwenden Sie eine
#temporary
Tabelle. Dies unterstützt die Erstellung von Statistiken, mit denen der Plan dynamisch gemäß den Daten neu kompiliert werden kann (obwohl für zwischengespeicherte temporäre Tabellen in gespeicherten Prozeduren das Neukompilierungsverhalten separat verstanden werden muss).
- Wenn sich der optimale Plan für die Abfrage unter Verwendung der Tabelle wahrscheinlich nie ändern wird, können Sie eine Tabellenvariable in Betracht ziehen, um den Aufwand für die Erstellung und Neukompilierung von Statistiken zu überspringen (möglicherweise sind Hinweise erforderlich, um den gewünschten Plan zu korrigieren).
- Wenn die Quelle für die in die Tabelle eingefügten Daten aus einer möglicherweise teuren
SELECT
Anweisung stammt, sollten Sie berücksichtigen, dass die Verwendung einer Tabellenvariablen die Möglichkeit der Verwendung eines parallelen Plans blockiert.
- Wenn Sie die Daten in der Tabelle benötigen, um ein Rollback einer äußeren Benutzertransaktion zu überstehen, verwenden Sie eine Tabellenvariable. Ein möglicher Anwendungsfall hierfür könnte darin bestehen, den Fortschritt verschiedener Schritte in einem langen SQL-Stapel zu protokollieren.
- Bei Verwendung einer
#temp
Tabelle innerhalb eines Benutzers können Transaktionssperren länger gehalten werden als für Tabellenvariablen (möglicherweise bis zum Ende der Transaktion im Vergleich zum Ende der Anweisung, abhängig von der Art der Sperre und der Isolationsstufe) und das Abschneiden des tempdb
Transaktionsprotokolls bis zum Benutzertransaktion endet. Dies könnte also die Verwendung von Tabellenvariablen begünstigen.
- Innerhalb gespeicherter Routinen können sowohl Tabellenvariablen als auch temporäre Tabellen zwischengespeichert werden. Die Metadatenpflege für zwischengespeicherte Tabellenvariablen ist geringer als für
#temporary
Tabellen. Bob Ward weist in seiner tempdb
Präsentation darauf hin, dass dies unter Bedingungen hoher Parallelität zu zusätzlichen Konflikten bei Systemtabellen führen kann. Darüber hinaus kann dies beim Umgang mit kleinen Datenmengen einen messbaren Unterschied zur Leistung bewirken .
Auswirkungen der Rowset-Freigabe
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
tempDB
- das "im Speicher" ist ein Mythos. Außerdem: Tabellenvariablen werden vom Abfrageoptimierer immer so betrachtet, dass sie genau eine Zeile enthalten. Wenn Sie viel mehr haben, kann dies zu ernsthaft schlechten Ausführungsplänen führen.