Was ist der Unterschied zwischen einer temporären Tabelle und einer Tabellenvariablen in SQL Server?

StackOverflow https://stackoverflow.com/questions/27894

Frage

In SQL Server 2005 können wir temporäre Tabellen auf zwei Arten erstellen:

declare @tmp table (Col1 int, Col2 int);

oder

create table #tmp (Col1 int, Col2 int);

Was sind die Unterschiede zwischen diesen beiden?Ich habe widersprüchliche Meinungen darüber gelesen, ob @tmp immer noch tempdb verwendet oder ob alles im Speicher passiert.

In welchen Szenarien übertrifft das eine das andere?

War es hilfreich?

Lösung

Es gibt einige Unterschiede zwischen temporären Tabellen (#tmp) und Tabellenvariablen (@tmp), obwohl die Verwendung von tempdb nicht dazu gehört, wie im MSDN-Link unten erläutert.

Als Faustregel gilt: Bei kleinen bis mittleren Datenmengen und einfachen Nutzungsszenarien sollten Sie Tabellenvariablen verwenden.(Dies ist eine zu weit gefasste Richtlinie mit natürlich vielen Ausnahmen – siehe unten und die folgenden Artikel.)

Einige Punkte, die Sie bei der Auswahl berücksichtigen sollten:

  • Temporäre Tabellen sind echte Tabellen, sodass Sie Dinge wie CREATE INDEXes usw. tun können.Wenn Sie große Datenmengen haben, auf die der Zugriff per Index schneller geht, sind temporäre Tabellen eine gute Option.

  • Tabellenvariablen können über PRIMARY KEY- oder UNIQUE-Einschränkungen Indizes haben.(Wenn Sie einen nicht eindeutigen Index wünschen, fügen Sie einfach die Primärschlüsselspalte als letzte Spalte in die Eindeutigkeitsbeschränkung ein.Wenn Sie keine eindeutige Spalte haben, können Sie eine Identitätsspalte verwenden.) SQL 2014 verfügt auch über nicht eindeutige Indizes.

  • Tabellenvariablen nehmen nicht an Transaktionen teil und SELECTs sind implizit mit NOLOCK.Das Transaktionsverhalten kann sehr hilfreich sein. Wenn Sie beispielsweise mitten in einer Prozedur ein ROLLBACK durchführen möchten, werden die während dieser Transaktion gefüllten Tabellenvariablen weiterhin gefüllt!

  • Temporäre Tabellen können dazu führen, dass gespeicherte Prozeduren möglicherweise häufig neu kompiliert werden.Tabellenvariablen werden dies nicht tun.

  • Sie können mit SELECT INTO eine temporäre Tabelle erstellen, die schneller zu schreiben ist (gut für Ad-hoc-Abfragen) und es Ihnen möglicherweise ermöglicht, mit sich ändernden Datentypen im Laufe der Zeit umzugehen, da Sie Ihre temporäre Tabellenstruktur nicht im Voraus definieren müssen.

  • Sie können Tabellenvariablen von Funktionen zurückgeben, wodurch Sie die Logik viel einfacher kapseln und wiederverwenden können (z. B. eine Funktion erstellen, um eine Zeichenfolge anhand eines beliebigen Trennzeichens in eine Wertetabelle aufzuteilen).

  • Durch die Verwendung von Tabellenvariablen in benutzerdefinierten Funktionen können diese Funktionen umfassender verwendet werden (Einzelheiten finden Sie in der CREATE FUNCTION-Dokumentation).Wenn Sie eine Funktion schreiben, sollten Sie Tabellenvariablen anstelle von temporären Tabellen verwenden, sofern keine zwingende Notwendigkeit besteht.

  • Sowohl Tabellenvariablen als auch temporäre Tabellen werden in tempdb gespeichert.Aber Tabellenvariablen (seit 2005) verwenden standardmäßig die Sortierung der aktuellen Datenbank im Vergleich zu temporären Tabellen, die die Standardsortierung von tempdb (ref).Dies bedeutet, dass Sie sich über Sortierungsprobleme im Klaren sein sollten, wenn Sie temporäre Tabellen verwenden und Ihre Datenbanksortierung sich von der tempdb unterscheidet. Dies führt zu Problemen, wenn Sie Daten in der temporären Tabelle mit Daten in Ihrer Datenbank vergleichen möchten.

  • Globale temporäre Tabellen (##tmp) sind eine weitere Art von temporären Tabellen, die allen Sitzungen und Benutzern zur Verfügung stehen.

Etwas weiterführende Literatur:

Andere Tipps

Schauen Sie sich nur die Behauptung in der akzeptierten Antwort an, dass Tabellenvariablen nicht an der Protokollierung beteiligt sind.

Es scheint im Allgemeinen unwahr zu sein, dass es einen Unterschied in der Menge der Protokollierung gibt (zumindest für insert/update/delete Operationen an der Tabelle selbst, obwohl ich habe seitdem gefunden dass es in dieser Hinsicht einen kleinen Unterschied für zwischengespeicherte temporäre Objekte in gespeicherten Prozeduren aufgrund zusätzlicher Systemtabellenaktualisierungen gibt).

Ich habe mir das Protokollierungsverhalten bei beiden angesehen @table_variable und ein #temp Tabelle für die folgenden Operationen.

  1. Erfolgreiche Einfügung
  2. Die Where-Anweisung zum Einfügen mehrerer Zeilen wurde aufgrund einer Einschränkungsverletzung zurückgesetzt.
  3. Aktualisieren
  4. Löschen
  5. Freigabe aufheben

Die Transaktionsprotokolldatensätze waren für alle Vorgänge nahezu identisch.

Die Tabellenvariablenversion hat tatsächlich einige extra Protokolleinträge, da ein Eintrag hinzugefügt (und später daraus entfernt) wird sys.syssingleobjrefs Basistabelle, aber insgesamt wurden ein paar Bytes weniger protokolliert, da der interne Name für Tabellenvariablen 236 Bytes weniger verbraucht als für #temp Tische (118 weniger nvarchar Figuren).

Vollständiges Skript zum Reproduzieren (am besten auf einer Instanz ausführen, die im Einzelbenutzermodus gestartet und verwendet wird sqlcmd Modus)

:setvar tablename "@T" 
:setvar tablescript "DECLARE @T TABLE"

/*
 --Uncomment this section to test a #temp table
:setvar tablename "#T" 
:setvar tablescript "CREATE TABLE #T"
*/

USE tempdb 
GO    
CHECKPOINT

DECLARE @LSN NVARCHAR(25)

SELECT @LSN = MAX([Current LSN])
FROM fn_dblog(null, null) 


EXEC(N'BEGIN TRAN StartBatch
SAVE TRAN StartBatch
COMMIT

$(tablescript)
(
[4CA996AC-C7E1-48B5-B48A-E721E7A435F0] INT PRIMARY KEY DEFAULT 0,
InRowFiller char(7000) DEFAULT ''A'',
OffRowFiller varchar(8000) DEFAULT REPLICATE(''B'',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast(''C'' as varchar(max)),10000)
)


BEGIN TRAN InsertFirstRow
SAVE TRAN InsertFirstRow
COMMIT

INSERT INTO $(tablename)
DEFAULT VALUES

BEGIN TRAN Insert9Rows
SAVE TRAN Insert9Rows
COMMIT


INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP 9 ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM sys.all_columns

BEGIN TRAN InsertFailure
SAVE TRAN InsertFailure
COMMIT


/*Try and Insert 10 rows, the 10th one will cause a constraint violation*/
BEGIN TRY
INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP (10) (10 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))) % 20
FROM sys.all_columns
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH

BEGIN TRAN Update10Rows
SAVE TRAN Update10Rows
COMMIT

UPDATE $(tablename)
SET InRowFiller = LOWER(InRowFiller),
    OffRowFiller  =LOWER(OffRowFiller),
    LOBFiller  =LOWER(LOBFiller)


BEGIN TRAN Delete10Rows
SAVE TRAN Delete10Rows
COMMIT

DELETE FROM  $(tablename)
BEGIN TRAN AfterDelete
SAVE TRAN AfterDelete
COMMIT

BEGIN TRAN EndBatch
SAVE TRAN EndBatch
COMMIT')


DECLARE @LSN_HEX NVARCHAR(25) = 
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 1, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 10, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 19, 4),2) AS INT) AS VARCHAR)        

SELECT 
    [Operation],
    [Context],
    [AllocUnitName],
    [Transaction Name],
    [Description]
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  

SELECT CASE
         WHEN GROUPING(Operation) = 1 THEN 'Total'
         ELSE Operation
       END AS Operation,
       Context,
       AllocUnitName,
       COALESCE(SUM([Log Record Length]), 0) AS [Size in Bytes],
       COUNT(*)                              AS Cnt
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  
GROUP BY GROUPING SETS((Operation, Context, AllocUnitName),())

Ergebnisse

+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
|                       |                    |                           |             @TV      |             #TV      |                  |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Operation             | Context            | AllocUnitName             | Size in Bytes | Cnt  | Size in Bytes | Cnt  | Difference Bytes |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| LOP_ABORT_XACT        | LCX_NULL           |                           | 52            | 1    | 52            | 1    |                  |
| LOP_BEGIN_XACT        | LCX_NULL           |                           | 6056          | 50   | 6056          | 50   |                  |
| LOP_COMMIT_XACT       | LCX_NULL           |                           | 2548          | 49   | 2548          | 49   |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 624           | 3    | 624           | 3    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 208           | 1    | 208           | 1    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrscols.clst        | 832           | 4    | 832           | 4    |                  |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL           |                           | 120           | 3    | 120           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 720           | 9    | 720           | 9    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.clust   | 444           | 3    | 444           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.nc      | 276           | 3    | 276           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.clst       | 628           | 4    | 628           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.nc         | 484           | 4    | 484           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.clst      | 176           | 1    | 176           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.nc        | 144           | 1    | 144           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.clst        | 100           | 1    | 100           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.nc1         | 88            | 1    | 88            | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysobjvalues.clst     | 596           | 5    | 596           | 5    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrowsets.clust      | 132           | 1    | 132           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrscols.clst        | 528           | 4    | 528           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.clst       | 1040          | 6    | 1276          | 6    | 236              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc1        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc2        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc3        | 480           | 6    | 480           | 6    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.clst | 96            | 1    |               |      | -96              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.nc1  | 88            | 1    |               |      | -88              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | Unknown Alloc Unit        | 72092         | 19   | 72092         | 19   |                  |
| LOP_DELETE_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 16348         | 37   | 16348         | 37   |                  |
| LOP_FORMAT_PAGE       | LCX_HEAP           | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_FORMAT_PAGE       | LCX_IAM            | Unknown Alloc Unit        | 252           | 3    | 252           | 3    |                  |
| LOP_FORMAT_PAGE       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 84            | 1    | 84            | 1    |                  |
| LOP_FORMAT_PAGE       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 4788          | 57   | 4788          | 57   |                  |
| LOP_HOBT_DDL          | LCX_NULL           |                           | 108           | 3    | 108           | 3    |                  |
| LOP_HOBT_DELTA        | LCX_NULL           |                           | 9600          | 150  | 9600          | 150  |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 456           | 3    | 456           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syscolpars.clst       | 644           | 4    | 644           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysidxstats.clst      | 180           | 1    | 180           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysiscols.clst        | 104           | 1    | 104           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysobjvalues.clst     | 616           | 5    | 616           | 5    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 136           | 1    | 136           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrscols.clst        | 544           | 4    | 544           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1064          | 6    | 1300          | 6    | 236              |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syssingleobjrefs.clst | 100           | 1    |               |      | -100             |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | Unknown Alloc Unit        | 135888        | 19   | 135888        | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysallocunits.nc      | 288           | 3    | 288           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syscolpars.nc         | 500           | 4    | 500           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysidxstats.nc        | 148           | 1    | 148           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysiscols.nc1         | 92            | 1    | 92            | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc1        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc2        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc3        | 504           | 6    | 504           | 6    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syssingleobjrefs.nc1  | 92            | 1    |               |      | -92              |
| LOP_INSERT_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 5112          | 71   | 5112          | 71   |                  |
| LOP_MARK_SAVEPOINT    | LCX_NULL           |                           | 508           | 8    | 508           | 8    |                  |
| LOP_MODIFY_COLUMNS    | LCX_CLUSTERED      | Unknown Alloc Unit        | 1560          | 10   | 1560          | 10   |                  |
| LOP_MODIFY_HEADER     | LCX_HEAP           | Unknown Alloc Unit        | 3780          | 45   | 3780          | 45   |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.syscolpars.clst       | 384           | 4    | 384           | 4    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysidxstats.clst      | 100           | 1    | 100           | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysrowsets.clust      | 92            | 1    | 92            | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1144          | 13   | 1144          | 13   |                  |
| LOP_MODIFY_ROW        | LCX_IAM            | Unknown Alloc Unit        | 4224          | 48   | 4224          | 48   |                  |
| LOP_MODIFY_ROW        | LCX_PFS            | Unknown Alloc Unit        | 13632         | 169  | 13632         | 169  |                  |
| LOP_MODIFY_ROW        | LCX_TEXT_MIX       | Unknown Alloc Unit        | 108640        | 120  | 108640        | 120  |                  |
| LOP_ROOT_CHANGE       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 960           | 10   | 960           | 10   |                  |
| LOP_SET_BITS          | LCX_GAM            | Unknown Alloc Unit        | 1200          | 20   | 1200          | 20   |                  |
| LOP_SET_BITS          | LCX_IAM            | Unknown Alloc Unit        | 1080          | 18   | 1080          | 18   |                  |
| LOP_SET_BITS          | LCX_SGAM           | Unknown Alloc Unit        | 120           | 2    | 120           | 2    |                  |
| LOP_SHRINK_NOOP       | LCX_NULL           |                           |               |      | 32            | 1    | 32               |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Total                 |                    |                           | 410144        | 1095 | 411232        | 1092 | 1088             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+

In welchen Szenarien übertrifft das eine das andere?

Verwenden Sie für kleinere Tabellen (weniger als 1000 Zeilen) eine temporäre Variable, andernfalls eine temporäre Tabelle.

@wcm – eigentlich ist die Tabellenvariable nicht nur RAM, sondern kann teilweise auf der Festplatte gespeichert werden.

Eine temporäre Tabelle kann Indizes haben, wohingegen eine Tabellenvariable nur einen Primärindex haben kann.Wenn die Geschwindigkeit ein Problem darstellt, können Tabellenvariablen schneller sein. Wenn jedoch viele Datensätze vorhanden sind oder die temporäre Tabelle eines Clustered-Index durchsucht werden muss, ist eine temporäre Tabelle natürlich besser.

Guter Hintergrundartikel

  1. Temperaturtabelle:Eine temporäre Tabelle ist einfach zu erstellen und Daten zu sichern.

    Tabellenvariable:Aber die Tabellenvariable ist mit dem Aufwand verbunden, den wir normalerweise beim Erstellen der normalen Tabellen haben.

  2. Temperaturtabelle:Das Ergebnis der temporären Tabelle kann von mehreren Benutzern verwendet werden.

    Tabellenvariable:Die Tabellenvariable kann jedoch nur vom aktuellen Benutzer verwendet werden.

  3. Temperaturtabelle:Die temporäre Tabelle wird in der Tempdb gespeichert.Es wird Netzwerkverkehr erzeugen.Wenn wir große Datenmengen in der temporären Tabelle haben, müssen diese in der gesamten Datenbank funktionieren.Es liegt ein Leistungsproblem vor.

    Tabellenvariable:Für einige der Daten wird jedoch eine Tabellenvariable im physischen Speicher gespeichert und später, wenn die Größe zunimmt, in die Tempdb verschoben.

  4. Temperaturtabelle:Die temporäre Tabelle kann alle DDL-Vorgänge ausführen.Es ermöglicht das Erstellen von Indizes, das Löschen, Ändern usw.

    Tabellenvariable:Während die Tabellenvariable die Durchführung der DDL-Operationen nicht zulässt.Mit der Tabellenvariablen können wir jedoch nur den Clustered-Index erstellen.

  5. Temperaturtabelle:Die temporäre Tabelle kann für die aktuelle Sitzung oder global verwendet werden.Damit eine Sitzung mit mehreren Benutzern die Ergebnisse in der Tabelle nutzen kann.

    Tabellenvariable:Die Tabellenvariable kann jedoch bis zu diesem Programm verwendet werden.(Gespeicherte Prozedur)

  6. Temperaturtabelle:Die temporäre Variable kann die Transaktionen nicht verwenden.Wenn wir die DML-Operationen mit der temporären Tabelle durchführen, kann es sich um ein Rollback oder einen Commit der Transaktionen handeln.

    Tabellenvariable:Für Tabellenvariablen können wir dies jedoch nicht tun.

  7. Temperaturtabelle:Funktionen können die temporäre Variable nicht verwenden.Darüber hinaus können wir die DML-Operation in den Funktionen nicht ausführen.

    Tabellenvariable:Aber die Funktion ermöglicht es uns, die Tabellenvariable zu verwenden.Aber mit der Tabellenvariablen können wir das machen.

  8. Temperaturtabelle:Die gespeicherte Prozedur führt die Neukompilierung durch (kann nicht denselben Ausführungsplan verwenden), wenn wir die temporäre Variable für alle nachfolgenden Aufrufe verwenden.

    Tabellenvariable:Während die Tabellenvariable das nicht tut.

Für alle unter Ihnen, die dem Mythos glauben, dass sich temporäre Variablen nur im Speicher befinden

Erstens ist die Tabellenvariable NICHT unbedingt speicherresident.Unter Speicherdruck können die zu einer Tabellenvariablen gehörenden Seiten nach tempdb verschoben werden.

Lesen Sie den Artikel hier: TempDB::Tabellenvariable vs. lokale temporäre Tabelle

Der andere Hauptunterschied besteht darin, dass Tabellenvariablen im Gegensatz zu temporären Tabellen keine Spaltenstatistiken haben.Das bedeutet, dass der Abfrageoptimierer nicht weiß, wie viele Zeilen sich in der Tabellenvariablen befinden (er vermutet 1), was dazu führen kann, dass äußerst nicht optimale Pläne generiert werden, wenn die Tabellenvariable tatsächlich über eine große Anzahl von Zeilen verfügt.

Zitat entnommen aus; Professionelle SQL Server 2012-Interna und Fehlerbehebung

StatistikenDer Hauptunterschied zwischen TEMP -Tabellen und Tabellenvariablen besteht darin, dass Statistiken nicht in Tabellenvariablen erstellt werden.Dies hat zwei Hauptkonsequenzen, deren Bestandteil der Abfrageoptimizer eine fi -XED -Schätzung für die Anzahl der Zeilen in einer Tabellenvariable verwendet, unabhängig von den von ihm enthaltenden Daten.Darüber hinaus ändert das Hinzufügen oder Entfernen von Daten die Schätzung nicht.

Indizes Sie können keine Indizes für Tabellenvariablen erstellen, obwohl Sie Einschränkungen erstellen können.Dies bedeutet, dass Sie durch das Erstellen von Primärschlüssel oder eindeutigen Einschränkungen Indizes (wie diese erstellt werden, um Einschränkungen zu unterstützen) in Tabellenvariablen haben.Selbst wenn Sie Einschränkungen haben und daher Indizes, die Statistiken haben, werden die Indizes nicht verwendet, wenn die Abfrage zusammengestellt wird, da sie nicht zum Kompilierzeit existieren, und sie verursachen auch Neukompilationen.

Schemaänderungen Schema -Modifikationen sind in temporären Tabellen möglich, nicht jedoch in Tabellenvariablen.Obwohl Schema -Modifikationen in temporären Tabellen möglich sind, vermeiden Sie diese Verwendung, da sie Neukunden von Aussagen verursachen, die die Tabellen verwenden.

Temporary Tables versus Table Variables

TABELLENVARIABLEN WERDEN NICHT IM SPEICHER ERSTELLT

Es gibt ein weit verbreitetes Missverständnis, dass Tabellenvariablen In-Memory-Strukturen sind und als solche schneller als temporäre Tabellen funktionieren.Vielen Dank an einen DMV namens Sys.DM _ DB _ Session _ Space _ Nutzung, die die Tempdb -Verwendung nach Sitzung zeigt, Sie können beweisen, dass das nicht der Fall ist.Führen Sie nach dem Neustart von SQL -Server zum Löschen des DMV das folgende Skript aus, um die Sitzung zu konfiieren, dass Ihre Sitzung _ ID 0 für Benutzer _ Objekte _ Alloc _ Seite _ Zählen Sie:

SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Jetzt können Sie überprüfen, wie viel Speicherplatz eine temporäre Tabelle verwendet, indem Sie das folgende Skript ausführen, um eine temporäre Tabelle mit einer Spalte zu erstellen und sie mit einer Zeile zu füllen:

CREATE TABLE #TempTable ( ID INT ) ;
INSERT INTO #TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Die Ergebnisse auf meinem Server zeigen, dass der Tabelle eine Seite in tempdb zugewiesen wurde.Führen Sie nun dasselbe Skript aus, verwenden Sie jedoch diesmal eine Tabellenvariable:

DECLARE @TempTable TABLE ( ID INT ) ;
INSERT INTO @TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Welches soll ich verwenden?

Ob Sie temporäre Tabellen oder Tabellenvariablen verwenden oder nicht, sollte durch gründliche Tests entschieden werden, aber jedoch Es ist am besten, sich für vorübergehend zu entscheiden Tabellen als Standard, da weitaus weniger Dinge entfernt werden können falsch.

Ich habe gesehen, wie Kunden Code mithilfe von Tabellenvariablen entwickelten, weil sie mit einer kleinen Anzahl von Zeilen zu tun hatten, und es war schneller als eine temporäre Tabelle, aber einige Jahre später gab es Hunderttausende von Zeilen in der Tabellenvariable und die Leistung war schrecklich Versuchen Sie also, eine Kapazitätsplanung zu ermöglichen, wenn Sie Ihre Entscheidung treffen!

Ein weiterer Unterschied:

Auf eine Tabellenvariable kann nur über Anweisungen innerhalb der Prozedur zugegriffen werden, die sie erstellt, nicht über andere Prozeduren, die von dieser Prozedur aufgerufen werden, oder über verschachteltes dynamisches SQL (über exec oder sp_executesql).

Der Geltungsbereich einer temporären Tabelle hingegen umfasst Code in aufgerufenen Prozeduren und verschachteltem dynamischem SQL.

Wenn die von Ihrer Prozedur erstellte Tabelle von anderen aufgerufenen Prozeduren oder dynamischem SQL aus zugänglich sein muss, müssen Sie eine temporäre Tabelle verwenden.Dies kann in komplexen Situationen sehr praktisch sein.

Bedenken Sie auch, dass Sie beide häufig durch abgeleitete Tabellen ersetzen können, was möglicherweise auch schneller ist.Wie bei jeder Leistungsoptimierung können Ihnen jedoch nur tatsächliche Tests anhand Ihrer tatsächlichen Daten den besten Ansatz für Ihre spezielle Abfrage ermitteln.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top