Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Wann sollte ich eine Tabellenvariable im Vergleich zu einer temporären Tabelle in SQL Server verwenden?

Ihre Frage zeigt, dass Sie einigen der verbreiteten Missverständnisse in Bezug auf Tabellenvariablen und temporäre Tabellen erlegen sind.

Ich habe auf der DBA-Site eine ziemlich ausführliche Antwort geschrieben, in der ich die Unterschiede zwischen den beiden Objekttypen betrachte. Dies beantwortet auch Ihre Frage zu Festplatte und Speicher (ich habe keinen signifikanten Unterschied im Verhalten zwischen den beiden festgestellt).

In Bezug auf die Frage im Titel, wann eine Tabellenvariable oder eine lokale temporäre Tabelle verwendet werden soll, haben Sie jedoch nicht immer die Wahl. In Funktionen ist es beispielsweise nur möglich, eine Tabellenvariable zu verwenden, und wenn Sie in einem untergeordneten Bereich in die Tabelle schreiben müssen, dann nur ein #temp table reicht (Tabellenwertparameter erlauben schreibgeschützten Zugriff).

Wo Sie die Wahl haben, finden Sie unten einige Vorschläge (obwohl die zuverlässigste Methode darin besteht, einfach beide mit Ihrer spezifischen Arbeitsbelastung zu testen).

  1. Wenn Sie einen Index benötigen, der nicht auf einer Tabellenvariablen erstellt werden kann, benötigen Sie natürlich einen #temporary Tisch. Die Details hierzu sind jedoch versionsabhängig. Für SQL Server 2012 und niedriger waren die einzigen Indizes, die für Tabellenvariablen erstellt werden konnten, diejenigen, die implizit durch einen UNIQUE erstellt wurden oder PRIMARY KEY Zwang. SQL Server 2014 hat die Inline-Indexsyntax für eine Teilmenge der Optionen eingeführt, die in CREATE INDEX verfügbar sind . 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.

  2. Wenn Sie wiederholt eine große Anzahl von Zeilen zur Tabelle hinzufügen und löschen, verwenden Sie einen #temporary Tisch. Das unterstützt TRUNCATE (was effizienter ist als DELETE für große Tabellen) und zusätzlich nachfolgende Einfügungen nach einem TRUNCATE können eine bessere Leistung haben als diejenigen, die auf DELETE folgen wie hier dargestellt.

  3. Wenn Sie eine große Anzahl von Zeilen löschen oder aktualisieren, kann die temporäre Tabelle möglicherweise eine viel bessere Leistung erbringen als eine Tabellenvariable - wenn sie die gemeinsame Nutzung von Rowsets verwenden kann (siehe "Auswirkungen der gemeinsamen Nutzung von Rowsets" unten für ein Beispiel). .
  4. Wenn der optimale Plan, der die Tabelle verwendet, abhängig von den Daten variiert, verwenden Sie einen #temporary Tisch. Dies unterstützt die Erstellung von Statistiken, die es ermöglichen, den Plan entsprechend den Daten dynamisch neu zu kompilieren (obwohl das Neukompilierungsverhalten für zwischengespeicherte temporäre Tabellen in gespeicherten Prozeduren separat verstanden werden muss).
  5. Wenn es unwahrscheinlich ist, dass sich der optimale Plan für die Abfrage unter Verwendung der Tabelle jemals ändert, können Sie eine Tabellenvariable in Betracht ziehen, um den Overhead der Statistikerstellung und Neukompilierung zu überspringen (würde möglicherweise Hinweise erfordern, um den gewünschten Plan zu korrigieren).
  6. Wenn die Quelle für die in die Tabelle eingefügten Daten aus einem möglicherweise teuren SELECT stammt Anweisung berücksichtigen Sie dann, dass die Verwendung einer Tabellenvariablen die Möglichkeit der Verwendung eines parallelen Plans blockiert.
  7. Wenn Sie die Daten in der Tabelle benötigen, um einen Rollback einer äußeren Benutzertransaktion zu überleben, verwenden Sie eine Tabellenvariable. Ein möglicher Anwendungsfall dafür könnte das Protokollieren des Fortschritts verschiedener Schritte in einem langen SQL-Batch sein.
  8. Bei Verwendung eines #temp Tabellen innerhalb einer Benutzertransaktionssperre können länger gehalten werden als für Tabellenvariablen (möglicherweise bis zum Ende der Transaktion vs. Ende der Anweisung, abhängig von der Art der Sperre und der Isolationsstufe) und es kann auch das Abschneiden des tempdb Transaktionsprotokoll, bis die Benutzertransaktion endet. Dies könnte also die Verwendung von Tabellenvariablen begünstigen.
  9. Innerhalb gespeicherter Routinen können sowohl Tabellenvariablen als auch temporäre Tabellen zwischengespeichert werden. Die Metadatenwartung für zwischengespeicherte Tabellenvariablen ist geringer als die für #temporary Tische. Bob Ward weist in seiner tempdb darauf hin Präsentation, dass dies unter Bedingungen hoher Parallelität zu zusätzlichen Konflikten in Systemtabellen führen kann. Darüber hinaus kann dies beim Umgang mit kleinen Datenmengen einen messbaren Leistungsunterschied bewirken.

Auswirkungen der gemeinsamen Nutzung von Rowsets

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