Dies scheint ein Gebiet mit einigen Mythen und widersprüchlichen Ansichten zu sein.
Was ist der Unterschied zwischen einer Tabellenvariablen und einer lokalen temporären Tabelle in SQL Server?
sql-server
t-sql
temporary-tables
Martin Smith
quelle
quelle
Antworten:
Inhalt
Vorbehalt
In dieser Antwort werden die in SQL Server 2000 eingeführten "klassischen" Tabellenvariablen erläutert. SQL Server 2014 im Speicher OLTP führt speicheroptimierte Tabellentypen ein. Tabellenvariableninstanzen davon unterscheiden sich in vielerlei Hinsicht von den unten diskutierten! ( weitere Details ).
Lagerraum
Kein Unterschied. Beide sind in gespeichert
tempdb
.Ich habe gesehen, dass dies für Tabellenvariablen nicht immer der Fall ist, dies kann jedoch anhand der folgenden Tabelle überprüft werden
Beispielergebnisse (Anzeige der Position in
tempdb
den 2 Zeilen werden gespeichert)Logischer Ort
@table_variables
Verhalten Sie sich mehr als ob sie Teil der aktuellen Datenbank wären als#temp
Tabellen. Für Spaltenkollatierungen von Tabellenvariablen (seit 2005) gilt, wenn nicht explizit angegeben, die der aktuellen Datenbank, während für#temp
Tabellen die Standardkollatierung vontempdb
( Weitere Details ) verwendet wird. Außerdem müssen benutzerdefinierte Datentypen und XML-Auflistungen in tempdb vorliegen, damit sie für#temp
Tabellen verwendet werden können. Tabellenvariablen können sie jedoch aus der aktuellen Datenbank ( Quelle ) verwenden.SQL Server 2012 führt enthaltene Datenbanken ein. das Verhalten von temporären Tabellen in diesen unterscheidet sich (h / t Aaron)
Sichtbarkeit für verschiedene Bereiche
@table_variables
Der Zugriff ist nur innerhalb des Stapels und Bereichs möglich, in dem sie deklariert sind.#temp_tables
sind in untergeordneten Stapeln zugänglich (verschachtelte Trigger, Prozeduren,exec
Aufrufe).#temp_tables
Am äußeren Gültigkeitsbereich (@@NESTLEVEL=0
) erstellte Objekte können sich auch über mehrere Stapel erstrecken, da sie bis zum Ende der Sitzung bestehen bleiben. Keiner der Objekttypen kann in einem untergeordneten Stapel erstellt werden und im aufrufenden Bereich aufgerufen werden, wie im Folgenden erläutert (es können jedoch auch globale##temp
Tabellen verwendet werden).Lebenszeit
@table_variables
werden implizit erstellt, wenn ein Stapel mit einerDECLARE @.. TABLE
Anweisung ausgeführt wird (bevor ein Benutzercode in diesem Stapel ausgeführt wird) und am Ende implizit gelöscht.Obwohl der Parser nicht zulässt, dass Sie versuchen, die Tabellenvariable vor der
DECLARE
Anweisung zu verwenden, wird die implizite Erstellung unten angezeigt.#temp_tables
werden explizit erstellt, wenn die TSQL-CREATE TABLE
Anweisung angetroffen wird, und können explizit mit gelöscht werdenDROP TABLE
oder werden implizit gelöscht, wenn der Stapel endet (wenn sie in einem untergeordneten Stapel mit erstellt wurden@@NESTLEVEL > 0
) oder wenn die Sitzung andernfalls endet.Hinweis: In gespeicherten Routinen können beide Objekttypen zwischengespeichert werden, anstatt wiederholt neue Tabellen zu erstellen und zu löschen. Es gibt jedoch Einschränkungen, wann dieses Caching auftreten kann, die möglicherweise verletzt werden
#temp_tables
, die jedoch durch die Einschränkungen auf@table_variables
jeden Fall verhindert werden. Der Wartungsaufwand für zwischengespeicherte#temp
Tabellen ist geringfügig höher als für Tabellenvariablen, wie hier dargestellt .Objekt-Metadaten
Dies ist im Wesentlichen für beide Objekttypen gleich. Es ist in den System-Basistabellen in gespeichert
tempdb
. Es ist einfacher, eine#temp
Tabelle zu suchen, daOBJECT_ID('tempdb..#T')
sie zum Eingeben der Systemtabellen verwendet werden kann und der intern generierte Name enger mit dem in derCREATE TABLE
Anweisung definierten Namen korreliert . Bei Tabellenvariablenobject_id
funktioniert die Funktion nicht und der interne Name wird vollständig vom System generiert, ohne dass eine Beziehung zum Variablennamen besteht. Das Folgende zeigt, dass die Metadaten immer noch vorhanden sind, indem Sie einen (hoffentlich eindeutigen) Spaltennamen eingeben. Für Tabellen ohne eindeutigen Spaltennamen kann die object_id mit bestimmt werdenDBCC PAGE
, solange sie nicht leer sind.Ausgabe
Transaktionen
Vorgänge für
@table_variables
werden als Systemtransaktionen ausgeführt, unabhängig von äußeren Benutzertransaktionen, wohingegen die entsprechenden#temp
Tabellenvorgänge als Teil der Benutzertransaktion selbst ausgeführt würden. Aus diesem Grund wirkt sich einROLLBACK
Befehl auf eine#temp
Tabelle aus, lässt die Tabelle jedoch@table_variable
unberührt.Protokollierung
Beide generieren Protokollsätze zum
tempdb
Transaktionsprotokoll. Ein häufiges Missverständnis ist, dass dies bei Tabellenvariablen nicht der Fall ist. Ein Skript, das dies demonstriert, deklariert eine Tabellenvariable, fügt ein paar Zeilen hinzu, aktualisiert sie und löscht sie.Da die Tabellenvariable zu Beginn und am Ende des Stapels implizit erstellt und gelöscht wird, müssen mehrere Stapel verwendet werden, um die vollständige Protokollierung anzuzeigen.
Kehrt zurück
Detaillierte Ansicht
Zusammenfassungsansicht (einschließlich Protokollierung für implizite Drop- und Systembasistabellen)
Soweit ich in der Lage war, Operationen an beiden zu unterscheiden, werden ungefähr die gleichen Mengen an Protokollierung generiert.
Während die Menge der Protokollierung ist sehr ähnlich ein wichtiger Unterschied ist im Zusammenhang , dass die Protokollaufzeichnungen
#temp
Tabellen nicht geräumt werden können , bis jede enthält Transaktion Benutzer beendet so eine lange Transaktion ausgeführt wird, dass zu einem bestimmten Zeitpunkt schreibt#temp
Tabellen Logkürzung in verhindern ,tempdb
während der autonomen Transaktionen für Tabellenvariablen erzeugt nicht.Tabellenvariablen werden nicht unterstützt,
TRUNCATE
daher kann dies zu einem Protokollierungsnachteil führen, wenn alle Zeilen aus einer Tabelle entfernt werden müssen (obwohl dies bei sehr kleinen TabellenDELETE
ohnehin besser funktionieren kann ).Kardinalität
Viele der Ausführungspläne mit Tabellenvariablen enthalten eine einzelne Zeile, die als Ausgabe von ihnen geschätzt wird. Die Untersuchung der Eigenschaften der Tabellenvariablen zeigt, dass SQL Server der Ansicht ist, dass die Tabellenvariable Null Zeilen enthält (Warum schätzt man, dass aus einer Null-Zeilen-Tabelle 1 Zeile ausgegeben wird, wird hier von @Paul White erläutert ).
Die im vorherigen Abschnitt gezeigten Ergebnisse zeigen jedoch eine genaue
rows
Zählung insys.partitions
. Das Problem ist, dass in den meisten Fällen die Anweisungen, die auf Tabellenvariablen verweisen, kompiliert werden, während die Tabelle leer ist. Wenn die Anweisung (neu) kompiliert wird, nachdem sie@table_variable
ausgefüllt wurde, wird dies stattdessen für die Kardinalität der Tabelle verwendetrecompile
.Der Plan zeigt die genaue geschätzte Zeilenanzahl nach der verzögerten Kompilierung.
In SQL Server 2012 SP2 wird das Ablaufverfolgungsflag 2453 eingeführt. Weitere Details finden Sie unter "Relational Engine" hier .
Wenn dieses Ablaufverfolgungsflag aktiviert ist, kann es dazu führen, dass die automatische Neukompilierung die geänderte Kardinalität berücksichtigt, wie in Kürze erläutert wird.
NB: Auf Azure in Kompatibilitätsstufe 150 wird die Kompilierung der Anweisung jetzt bis zur ersten Ausführung zurückgestellt . Dies bedeutet, dass das Problem der Nullzeilenschätzung nicht mehr auftritt.
Keine Spaltenstatistik
Eine genauere Tabellenkardinalität bedeutet jedoch nicht, dass die geschätzte Zeilenzahl genauer ist (es sei denn, Sie führen eine Operation für alle Zeilen in der Tabelle aus). SQL Server verwaltet überhaupt keine Spaltenstatistiken für Tabellenvariablen und greift daher auf Schätzungen zurück, die auf dem Vergleichsprädikat basieren (z. B. dass 10% der Tabelle für
=
eine nicht eindeutige Spalte oder 30% für einen>
Vergleich zurückgegeben werden). Im Gegensatz Spaltenstatistiken werden für beibehalten#temp
Tabellen.SQL Server zählt die Anzahl der an jeder Spalte vorgenommenen Änderungen. Wenn die Anzahl der Änderungen seit dem Kompilieren des Plans den Neukompilierungsschwellenwert (RT) überschreitet, wird der Plan neu kompiliert und die Statistik aktualisiert. Die RT hängt vom Tabellentyp und der Größe ab.
Aus Plan Caching in SQL Server 2008
Der
KEEP PLAN
Hinweis kann verwendet werden, um die RT für#temp
Tabellen wie für permanente Tabellen festzulegen.Der Nettoeffekt davon ist, dass die für
#temp
Tabellen generierten Ausführungspläne häufig um Größenordnungen besser sind, als@table_variables
wenn viele Zeilen beteiligt sind, da SQL Server bessere Informationen zum Arbeiten hat.NB1: Tabellenvariablen haben keine Statistik, können jedoch ein Neukompilierungsereignis "Statistik geändert" unter dem Ablaufverfolgungsflag 2453 verursachen (gilt nicht für "triviale" Pläne). Dies scheint unter denselben Neukompilierungsschwellenwerten zu erfolgen, wie oben für temporäre Tabellen mit einem angegeben zusätzliche, wenn
N=0 -> RT = 1
. dh alle Anweisungen, die kompiliert werden, wenn die Tabellenvariable leer ist, erhalten eine Neukompilierung und werden korrigiert,TableCardinality
wenn sie das erste Mal ausgeführt werden, wenn sie nicht leer sind. Die Kardinalität des Kompilierungszeitplans wird im Plan gespeichert. Wenn die Anweisung erneut mit derselben Kardinalität ausgeführt wird (entweder aufgrund von Steueranweisungen oder der Wiederverwendung eines zwischengespeicherten Plans), erfolgt keine Neukompilierung.Anmerkung 2: Für zwischengespeicherte temporäre Tabellen in gespeicherten Prozeduren ist die Rekompilierungsgeschichte viel komplizierter als oben beschrieben. Alle wichtigen Details finden Sie unter Temporäre Tabellen in gespeicherten Prozeduren .
Kompiliert neu
Sowie die Modifikation basiert Neukompilierungen oben beschriebenen
#temp
Tabellen können auch mit in Verbindung gebracht werden zusätzliche compiliert , nur weil sie Operationen ermöglichen , die für Tabellenvariablen verboten sind , die eine Kompilierung (zB DDL Änderungen auslösenCREATE INDEX
,ALTER TABLE
)Verriegelung
Es wurde angegeben, dass Tabellenvariablen nicht am Sperren teilnehmen. Das ist nicht der Fall. Durch Ausführen der folgenden Ausgaben auf der Registerkarte SSMS-Nachrichten werden die Details der Sperren angezeigt, die für eine insert-Anweisung verwendet und freigegeben wurden.
Bei Abfragen, die
SELECT
von Tabellenvariablen stammen, weist Paul White in den Kommentaren darauf hin, dass diese automatisch mit einem implizitenNOLOCK
Hinweis versehen sind. Dies ist unten dargestelltAusgabe
Die Auswirkung auf das Sperren ist jedoch möglicherweise recht gering.
Keine dieser Rückgaben führt zu einer Indexschlüsselreihenfolge, die angibt, dass SQL Server für beide einen nach Zuordnung sortierten Scan verwendet hat .
Ich habe das obige Skript zweimal ausgeführt und die Ergebnisse für den zweiten Durchlauf sind unten
Die Sperrausgabe für die Tabellenvariable ist in der Tat äußerst gering, da SQL Server nur eine Schemastabilitätssperre für das Objekt erhält. Aber für einen
#temp
Tisch ist es fast so leicht, dass er eineS
Sperre auf Objektebene aufhebt. EinNOLOCK
Hinweis oder eineREAD UNCOMMITTED
Isolationsstufe kann natürlich auch beim Arbeiten mit#temp
Tabellen explizit angegeben werden .Ähnlich wie bei der Protokollierung kann eine umgebende Benutzertransaktion dazu führen, dass die Sperren für
#temp
Tabellen länger gehalten werden. Mit dem Skript untenWenn Sie in beiden Fällen außerhalb einer expliziten Benutzertransaktion ausgeführt werden,
sys.dm_tran_locks
wird bei der Überprüfung nur eine gemeinsame Sperre für das zurückgegebenDATABASE
.Beim Aufheben des Kommentars werden die
BEGIN TRAN ... ROLLBACK
26 Zeilen zurückgegeben. Dies zeigt, dass Sperren sowohl für das Objekt selbst als auch für Systemtabellenzeilen bestehen, um ein Rollback zu ermöglichen und zu verhindern, dass andere Transaktionen nicht festgeschriebene Daten lesen. Die entsprechende Tabellenvariablenoperation unterliegt bei der Benutzertransaktion keinem Rollback und muss diese Sperren nicht halten, damit wir die nächste Anweisung einchecken können. Die Ablaufverfolgung von Sperren, die im Profiler erworben und freigegeben wurden oder die das Ablaufverfolgungsflag 1200 verwenden, zeigt jedoch, dass noch viele Sperrereignisse vorhanden sind auftreten.Indizes
In Versionen vor SQL Server 2014 können Indizes nur implizit für Tabellenvariablen als Nebeneffekt des Hinzufügens einer eindeutigen Einschränkung oder eines eindeutigen Primärschlüssels erstellt werden. Dies bedeutet natürlich, dass nur eindeutige Indizes unterstützt werden. Ein nicht eindeutiger nicht gruppierter Index für eine Tabelle mit einem eindeutigen gruppierten Index kann jedoch simuliert werden, indem er einfach deklariert
UNIQUE NONCLUSTERED
und der CI-Schlüssel am Ende des gewünschten NCI-Schlüssels hinzugefügt wird (SQL Server würde dies ohnehin hinter den Kulissen tun, selbst wenn ein nicht eindeutiger Index vorliegt NCI kann angegeben werden)Wie bereits gezeigt, können verschiedene
index_option
s in der Constraint-Deklaration angegeben werden, einschließlichDATA_COMPRESSION
,IGNORE_DUP_KEY
undFILLFACTOR
(obwohl es keinen Sinn macht, diese zu setzen, da dies nur einen Unterschied bei der Index-Neuerstellung macht und Sie keine Indizes für Tabellenvariablen neu erstellen können!)Darüber hinaus unterstützen Tabellenvariablen keine
INCLUDE
d-Spalten, gefilterten Indizes (bis 2016) oder Partitionierung,#temp
Tabellen jedoch (das Partitionsschema muss erstellt werdentempdb
).Indizes in SQL Server 2014
Nicht eindeutige Indizes können in der Definition der Tabellenvariablen in SQL Server 2014 als Inline deklariert werden. Die Beispielsyntax hierfür ist unten aufgeführt.
Indizes in SQL Server 2016
Ab CTP 3.1 ist es jetzt möglich, gefilterte Indizes für Tabellenvariablen zu deklarieren. Bei RTM ist es möglich , dass auch eingeschlossene Spalten zulässig sind, obwohl sie es aufgrund von Ressourcenbeschränkungen wahrscheinlich nicht in SQL16 schaffen
Parallelität
Abfragen, die in
@table_variables
einen parallelen Plan eingefügt (oder auf andere Weise geändert) werden können,#temp_tables
sind auf diese Weise nicht eingeschränkt.Es gibt eine offensichtliche Problemumgehung, die das Umschreiben wie folgt ermöglicht: Der
SELECT
Teil kann parallel ausgeführt werden, es wird jedoch eine verborgene temporäre Tabelle verwendet (hinter den Kulissen).Es gibt keine solche Einschränkung bei Abfragen, die aus Tabellenvariablen auswählen , wie in meiner Antwort hier dargestellt
Andere funktionale Unterschiede
#temp_tables
Kann nicht in einer Funktion verwendet werden.@table_variables
Kann in skalaren oder Tabellen-UDFs mit mehreren Anweisungen verwendet werden.@table_variables
kann keine benannten Bedingungen haben.@table_variables
kann nichtSELECT
-edINTO
,ALTER
-ed,TRUNCATE
d oder das Ziel vonDBCC
Befehlen wieDBCC CHECKIDENT
oder von seinSET IDENTITY INSERT
und unterstützt keine Tabellenhinweise wieWITH (FORCESCAN)
CHECK
Einschränkungen für Tabellenvariablen werden vom Optimierer zur Vereinfachung, für implizite Prädikate oder zur Erkennung von Widersprüchen nicht berücksichtigt.PAGELATCH_EX
Wartezeiten führen. ( Beispiel )Nur Speicher?
Wie eingangs erwähnt, werden beide auf Seiten in gespeichert
tempdb
. Ich habe jedoch nicht angesprochen, ob es einen Unterschied im Verhalten beim Schreiben dieser Seiten auf Disc gibt.Ich habe dies jetzt ein wenig getestet und bisher keinen solchen Unterschied festgestellt. In dem spezifischen Test, den ich auf meiner Instanz von SQL Server 250 durchgeführt habe, scheint es, dass Seiten der Cut-Off-Punkt sind, bevor die Datendatei geschrieben wird.
Führen Sie das folgende Skript aus
Und Überwachung schreibt in die
tempdb
Datendatei mit Prozessmonitor Ich sah keine (außer gelegentlich auf der Datenbank-Boot-Seite bei Versatz 73.728). Nachdem ich zu gewechselt hatte250
,251
begann ich, die folgenden Schriften zu sehen.Der obige Screenshot zeigt 5 * 32 Seitenschreibvorgänge und einen einzelnen Seitenschreibvorgang, der angibt, dass 161 der Seiten auf eine Disc geschrieben wurden. Ich habe den gleichen Grenzwert von 250 Seiten beim Testen mit Tabellenvariablen erhalten. Das folgende Skript zeigt es auf eine andere Art und Weise
sys.dm_os_buffer_descriptors
Ergebnisse
Es wurde angezeigt, dass 192 Seiten auf die Disc geschrieben und die unsaubere Flagge gelöscht wurde. Es zeigt auch, dass das Beschreiben von Datenträgern nicht bedeutet, dass Seiten sofort aus dem Pufferpool entfernt werden. Die Abfragen für diese Tabellenvariable konnten immer noch vollständig aus dem Speicher ausgeführt werden.
Auf einem inaktiven Server
max server memory
, auf dem Pufferpoolseiten festgelegt2000 MB
undDBCC MEMORYSTATUS
gemeldet sind, die als ca. 1.843.000 KB (ca. 23.000 Seiten) zugewiesen sind, habe ich in Stapeln von 1.000 Zeilen / Seiten in die obigen Tabellen eingefügt und für jede Iteration aufgezeichnet.Sowohl die Tabellenvariable als auch die
#temp
Tabelle gaben nahezu identische Diagramme wieder und konnten den Pufferpool so gut wie ausschöpfen, bevor sie zu dem Punkt gelangten, dass sie nicht vollständig im Arbeitsspeicher gespeichert waren, sodass es keine besondere Einschränkung hinsichtlich des Arbeitsspeichers zu geben scheint beides kann verbrauchen.quelle
Es gibt ein paar Dinge, auf die ich eher aufgrund bestimmter Erfahrungen hinweisen möchte, als dass ich sie studiere. Als DBA bin ich sehr neu. Bitte korrigieren Sie mich, wo erforderlich.
quelle