Wann sollte ich eine Tabellenvariable gegenüber einer temporären Tabelle in SQL Server verwenden?

298

Ich lerne mehr Details in der Tabellenvariablen. Es heißt, dass temporäre Tabellen immer auf der Festplatte sind und sich Tabellenvariablen im Speicher befinden, dh die Leistung der Tabellenvariablen ist besser als die der temporären Tabelle, da die Tabellenvariable weniger E / A-Operationen als die temporäre Tabelle verwendet.

Aber manchmal, wenn eine Tabellenvariable zu viele Datensätze enthält, die nicht im Speicher enthalten sein können, wird die Tabellenvariable wie die temporäre Tabelle auf die Festplatte gelegt.

Aber ich weiß nicht, was die "zu vielen Platten" sind. 100.000 Datensätze? oder 1000.000 Datensätze? Wie kann ich feststellen, ob sich eine von mir verwendete Tabellenvariable im Speicher oder auf der Festplatte befindet? Gibt es in SQL Server 2005 eine Funktion oder ein Tool, mit dem Sie die Skalierung der Tabellenvariablen messen oder wissen lassen können, wann die Tabellenvariable aus dem Speicher auf die Festplatte gestellt wird?

Yman
quelle
5
Eine Tabellenvariable ist fast immer in 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.
Marc_s
Sie können diese hilfreiche stackoverflow.com/questions/27894/…
Igor
2
@marc_s - Sie können das "fast" in dieser Anweisung löschen. Es ist immer in tempdb(aber kann auch ganz in Erinnerung bleiben)
Martin Smith
2
Mit SQL 2014 können Sie jetzt eine Tabellenvariable im Speicher erstellen
Paparazzo

Antworten:

362

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 #tempTabelle 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).

  1. Wenn Sie einen Index benötigen, der nicht für eine Tabellenvariable erstellt werden kann, benötigen Sie natürlich eine #temporaryTabelle. 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 UNIQUEoder 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.

  2. Wenn Sie wiederholt eine große Anzahl von Zeilen zur Tabelle hinzufügen und daraus löschen, verwenden Sie eine #temporaryTabelle. Dies unterstützt TRUNCATE(was effizienter ist als DELETEbei großen Tabellen) und zusätzlich nachfolgende Einfügungen nach a TRUNCATEkönnen eine bessere Leistung haben als solche nach a, DELETE wie hier dargestellt .

  3. 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").
  4. Wenn der optimale Plan unter Verwendung der Tabelle abhängig von den Daten variiert, verwenden Sie eine #temporaryTabelle. 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).
  5. 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).
  6. Wenn die Quelle für die in die Tabelle eingefügten Daten aus einer möglicherweise teuren SELECTAnweisung stammt, sollten Sie berücksichtigen, 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 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.
  8. Bei Verwendung einer #tempTabelle 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 tempdbTransaktionsprotokolls bis zum 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 Metadatenpflege für zwischengespeicherte Tabellenvariablen ist geringer als für #temporaryTabellen. Bob Ward weist in seiner tempdbPrä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
Martin Smith
quelle
2
Hallo, Herr Martin Smith. In meinem Fall möchte ich nur eine Reihe von IDs-Werten speichern, um sie in anderen Abfragen innerhalb der Store-Prozedur zu verwenden. Was empfehlen Sie mir?
Jeancarlo Fontalvo
@JeancarloFontalvo - eine Tabellenvariable mit einem Primärschlüssel idund der Verwendung von OPTION (RECOMPILE)wäre wahrscheinlich in Ordnung - aber testen Sie beide.
Martin Smith
Ist der Metadatenkonflikt für die temporäre Tabelle und die Tabellenvariable gleich?
Syed Aqeel Ashiq
@Syed. Generell weniger fürs Fernsehen. Sperren können früher freigegeben werden, wenn sie sich in einer Benutzertransaktion befinden. Siehe auch den Bob Ward Link.
Martin Smith
73

Verwenden Sie eine Tabellenvariable, wenn für eine sehr kleine Datenmenge (Tausende von Bytes)

Verwenden Sie eine temporäre Tabelle für viele Daten

Eine andere Möglichkeit, darüber nachzudenken: Wenn Sie der Meinung sind, dass Sie von einem Index, automatisierten Statistiken oder einer SQL-Optimierungsfunktion profitieren könnten, ist Ihr Datensatz wahrscheinlich zu groß für eine Tabellenvariable.

In meinem Beispiel wollte ich nur ungefähr 20 Zeilen in ein Format einfügen und sie als Gruppe ändern, bevor ich sie zum UPDATE / INSERT einer permanenten Tabelle verwendete. Eine Tabellenvariable ist also perfekt.

Ich führe aber auch SQL aus, um Tausende von Zeilen gleichzeitig zu füllen, und ich kann definitiv sagen, dass die temporären Tabellen eine viel bessere Leistung als Tabellenvariablen aufweisen.

Dies ist nicht unähnlich, wie CTEs aus einem ähnlichen Grund ein Problem darstellen. Wenn die Daten im CTE sehr klein sind, finde ich, dass ein CTE genauso gut oder besser abschneidet als das, was der Optimierer vorschlägt, aber wenn er dann ziemlich groß ist es tut dir weh

Mein Verständnis basiert hauptsächlich auf http://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/ , das viel detaillierter ist.

Abakus
quelle
Die Tabellenvariable zum Mitnehmen ist für kleine Datenmengen in Ordnung, verwenden Sie jedoch die temporäre Tabelle für größere Datenmengen. Ich habe eine Abfrage mit Tausenden von Zeilen. Durch den Wechsel von der Tabellenvariablen zur temporären Tabelle wird die Abfragezeit von 40 auf nur 5 Sekunden verkürzt, wobei alles andere gleich ist.
Liang
42

Microsoft sagt hier

Tabellenvariablen haben keine Verteilungsstatistik, sie lösen keine Neukompilierungen aus. In vielen Fällen erstellt der Optimierer daher einen Abfrageplan unter der Annahme, dass die Tabellenvariable keine Zeilen enthält. Aus diesem Grund sollten Sie bei der Verwendung einer Tabellenvariablen vorsichtig sein, wenn Sie eine größere Anzahl von Zeilen erwarten (größer als 100). Temp-Tabellen können in diesem Fall eine bessere Lösung sein.

Paul Sturm
quelle
14

Ich stimme Abacus voll und ganz zu (sorry - ich habe nicht genug Punkte, um einen Kommentar abzugeben).

Denken Sie auch daran, dass es nicht unbedingt darauf ankommt, wie viele Datensätze Sie haben, sondern auf die Größe Ihrer Datensätze.

Haben Sie beispielsweise den Leistungsunterschied zwischen 1.000 Datensätzen mit jeweils 50 Spalten und 100.000 Datensätzen mit jeweils nur 5 Spalten berücksichtigt?

Schließlich fragen Sie vielleicht mehr Daten ab, als Sie benötigen? Hier finden Sie eine gute Lektüre zu SQL-Optimierungsstrategien . Begrenzen Sie die Datenmenge, die Sie abrufen, insbesondere wenn Sie nicht alles verwenden (einige SQL-Programmierer werden faul und wählen einfach alles aus, obwohl sie nur eine winzige Teilmenge verwenden). Vergessen Sie nicht, dass der SQL Query Analyzer möglicherweise auch Ihr bester Freund wird.


quelle
4

Die variable Tabelle ist nur für die aktuelle Sitzung verfügbar. Wenn Sie beispielsweise eine EXECandere gespeicherte Prozedur innerhalb der aktuellen benötigen, müssen Sie die Tabelle übergeben, da Table Valued Parameterdies natürlich die Leistung beeinträchtigt. Bei temporären Tabellen können Sie dies nur tun Übergabe des temporären Tabellennamens

So testen Sie eine temporäre Tabelle:

  • Öffnen Sie den Management Studio-Abfrageeditor
  • Erstellen Sie eine temporäre Tabelle
  • Öffnen Sie ein anderes Abfrageeditorfenster
  • Wählen Sie aus dieser Tabelle "Verfügbar"

So testen Sie eine Variablentabelle:

  • Öffnen Sie den Management Studio-Abfrageeditor
  • Erstellen Sie eine Variablentabelle
  • Öffnen Sie ein anderes Abfrageeditorfenster
  • Wählen Sie aus dieser Tabelle "Nicht verfügbar".

Ich habe Folgendes erlebt: Wenn Ihr Schema keine GRANTBerechtigung zum Erstellen von Tabellen hat, verwenden Sie variable Tabellen.

Mina Gabriel
quelle
3

Beim Schreiben von Daten in deklarierte Tabellen declare @tbund nach dem Verknüpfen mit anderen Tabellen wurde mir klar, dass die Antwortzeit im Vergleich zu temporären Tabellen tempdb .. # tbviel höher ist.

Wenn ich sie mit @tb verbinde, ist die Zeit für die Rückgabe des Ergebnisses viel länger als bei #tm . Die Rückgabe erfolgt fast augenblicklich.

Ich habe Tests mit einem Join von 10.000 Zeilen und einem Join mit 5 anderen Tabellen durchgeführt

César Augusto
quelle
Könnten Sie den Test veröffentlichen, den Sie durchgeführt haben, um diese Zahlen zu erhalten?
Dan Def