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


390

In SQL Server 2005 können Sie 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 noch Tempdb verwendet oder ob alles im Speicher passiert.

In welchen Szenarien übertrifft einer den anderen?



2
Es gibt einen wirklich guten Artikel
sam yi

Antworten:


392

Es gibt einige Unterschiede zwischen temporären Tabellen (#tmp) und Tabellenvariablen (@tmp), obwohl die Verwendung von tempdb keine davon ist, wie im folgenden MSDN-Link beschrieben.

Als Faustregel gilt, dass Sie für kleine bis mittlere Datenmengen und einfache Verwendungsszenarien Tabellenvariablen verwenden sollten. (Dies ist eine zu breite Richtlinie mit natürlich vielen Ausnahmen - siehe unten und folgende Artikel.)

Einige Punkte, die bei der Auswahl zu beachten sind:

  • Temporäre Tabellen sind echte Tabellen, sodass Sie beispielsweise INDEXE ERSTELLEN usw. können. Wenn Sie über große Datenmengen verfügen, für die der Zugriff per Index schneller ist, sind temporäre Tabellen eine gute Option.

  • Tabellenvariablen können mithilfe von PRIMARY KEY- oder UNIQUE-Einschränkungen Indizes haben. (Wenn Sie einen nicht eindeutigen Index möchten, fügen Sie einfach die Primärschlüsselspalte als letzte Spalte in die eindeutige Einschrä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 ROLLBACKEN möchten, werden die während dieser Transaktion aufgefüllten Tabellenvariablen weiterhin ausgefüllt.

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

  • Sie können eine temporäre Tabelle mit SELECT INTO erstellen, die schneller zu schreiben ist (gut für Ad-hoc-Abfragen geeignet) und es Ihnen möglicherweise ermöglicht, sich im Laufe der Zeit mit sich ändernden Datentypen zu befassen, 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 zum Teilen einer Zeichenfolge in eine Wertetabelle in einem beliebigen Trennzeichen erstellen).

  • Durch die Verwendung von Tabellenvariablen in benutzerdefinierten Funktionen können diese Funktionen häufiger verwendet werden (Einzelheiten finden Sie in der Dokumentation zu CREATE FUNCTION). Wenn Sie eine Funktion schreiben, sollten Sie Tabellenvariablen über temporäre Tabellen verwenden, sofern nicht zwingend etwas anderes erforderlich ist.

  • Sowohl Tabellenvariablen als auch temporäre Tabellen werden in tempdb gespeichert. Tabellenvariablen (seit 2005) verwenden standardmäßig die Sortierung der aktuellen Datenbank im Vergleich zu temporären Tabellen, die die Standardkollatierung von tempdb ( ref ) verwenden. Dies bedeutet, dass Sie sich der Sortierprobleme bewusst sein sollten, wenn Sie temporäre Tabellen verwenden und Ihre Datenbankkollatierung sich von der temporären 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 andere Art von temporären Tabellen, die allen Sitzungen und Benutzern zur Verfügung stehen.

Einige weiterführende Literatur:


26
Tabellenvariablen können Indizes haben. Erstellen Sie einfach eine eindeutige Einschränkung, und Sie erhalten automatisch einen Index. Macht einen großen Leistungsunterschied. (Wenn Sie keinen eindeutigen Index möchten, fügen Sie einfach den eigentlichen Primärschlüssel am Ende der gewünschten Felder hinzu. Wenn Sie keinen haben, erstellen Sie eine Identitätsspalte.)
Ben

7
@ Ben And SQL Server 2014 ermöglicht die Angabe
Martin Smith

4
Tabellenvariablen, die nicht von Transaktionen betroffen sind, sind manchmal praktisch. Wenn Sie nach einem Rollback etwas behalten möchten, können Sie es in eine Tabellenvariable einfügen.
Federbrecher

3
Statistiken werden für temporäre Tabellen erstellt, wodurch Abfragepläne verbessert werden können, nicht jedoch für Tabellenvariablen. Diese Statistiken werden zusammen mit den Seiten der temporären Tabelle für eine Weile zwischengespeichert, nachdem die temporäre Tabelle gelöscht wurde, und können ungenau sein, wenn die zwischengespeicherte Tabelle erneut aktiviert wird.
Michael Green

Tabellenvariablen verwenden standardmäßig entweder die Sortierung des benutzerdefinierten Datentyps (wenn die Spalte einen benutzerdefinierten Datentyp hat) oder die Sortierung der aktuellen Datenbank und nicht die Standardkollatierung der temporären Datenbank. Temp-Tabellen verwenden die Standardkollatierung tempdb. Siehe: technet.microsoft.com/en-us/library/ms188927.aspx
PseudoToad

25

Betrachten Sie nur die Behauptung in der akzeptierten Antwort, dass Tabellenvariablen nicht an der Protokollierung teilnehmen.

Es scheint im Allgemeinen nicht wahr zu sein, dass es einen Unterschied in der Menge der Protokollierung gibt (zumindest für insert/ update/ deleteOperationen an der Tabelle selbst, obwohl ich seitdem festgestellt habe, dass diesbezüglich einige kleine Unterschiede für zwischengespeicherte temporäre Objekte in gespeicherten Prozeduren aufgrund zusätzlicher Systemtabellen bestehen Aktualisierung).

Ich habe das Protokollierungsverhalten sowohl für eine @table_variableals auch für eine #tempTabelle für die folgenden Vorgänge untersucht.

  1. Erfolgreiches Einfügen
  2. Mehrzeilig Einfügen, wo die Anweisung aufgrund einer Einschränkungsverletzung zurückgesetzt wurde.
  3. Aktualisieren
  4. Löschen
  5. Freigeben

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

Die Tabellenvariablenversion enthält tatsächlich einige zusätzliche Protokolleinträge, da ein Eintrag zur Basistabelle hinzugefügt (und später aus dieser entfernt) wird. sys.syssingleobjrefsInsgesamt wurden jedoch einige weniger Bytes protokolliert, da der interne Name für Tabellenvariablen 236 Bytes weniger verbraucht als für #tempTabellen (118 weniger nvarcharZeichen).

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

: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             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+

1
+1 Nur aus Neugier (und um ein wenig pedantisch zu sein). Die Frage ist / war ziemlich alt (August 2008), es ging also um SQL 2005. Jetzt sind wir im Jahr 2011 (Ende) und das neueste SQL ist 2008 R2 plus die Denali-Beta. Welche Version haben Sie verwendet?
Xanatos

2
@xanatos - 2008. Bei 2005 wären Tabellenvariablen tatsächlich im Nachteil, da sie INSERT ... SELECTnicht minimal protokolliert wurden und Sie keine SELECT INTO ... Tabellenvariable können.
Martin Smith

1
Vielen Dank an @MartinSmith, meine Antwort wurde aktualisiert, um den Anspruch auf Protokollierung zu entfernen.
Rory

18

In welchen Szenarien übertrifft einer den anderen?

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


17
Unterstützende Daten? Dies ist nicht nur als Behauptung für sich genommen sehr hilfreich.
Michael Myers

8
Microsoft empfiehlt eine Beschränkung auf 100 Zeilen: msdn.microsoft.com/en-us/library/ms175010.aspx (siehe Abschnitt "Best Practices").
Artemix

Siehe meine Antwort unten für eine Erklärung.
Weihui Guo

17

@wcm - um die Tabellenvariable nicht auszuwählen, ist sie nicht nur Ram - sie kann teilweise auf der Festplatte gespeichert werden.

Eine temporäre Tabelle kann Indizes haben, während eine Tabellenvariable nur einen Primärindex haben kann. Wenn Geschwindigkeit ein Problem ist, 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 besser.

Guter Hintergrundartikel


2
Guter Hintergrundartikel +1. Ich werde meine Antwort löschen, da das Ändern nicht viel hinterlassen würde und es bereits so viele gute Antworten gibt
wcm

12
  1. Temp-Tabelle: Eine Temp-Tabelle ist einfach zu erstellen und Daten zu sichern.

    Tabellenvariable: Die Tabellenvariable ist jedoch mit dem Aufwand verbunden, wenn normalerweise die normalen Tabellen erstellt werden.

  2. Temp-Tabelle: Das Ergebnis der Temp-Tabelle kann von mehreren Benutzern verwendet werden.

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

  3. Temp-Tabelle: Die Temp-Tabelle wird in der Tempdb gespeichert. Es wird Netzwerkverkehr machen. Wenn die temporäre Tabelle große Datenmengen enthält, muss diese in der gesamten Datenbank funktionieren. Es liegt ein Leistungsproblem vor.

    Tabellenvariable: Eine Tabellenvariable wird jedoch für einige Daten im physischen Speicher gespeichert. Später, wenn die Größe zunimmt, wird sie in die temporäre Datenbank verschoben.

  4. Temp-Tabelle: Die Temp-Tabelle kann alle DDL-Operationen ausführen. Es ermöglicht das Erstellen der Indizes, das Löschen, Ändern usw.

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

  5. Temp-Tabelle: Die Temp-Tabelle kann für die aktuelle Sitzung oder global verwendet werden. Damit eine Sitzung mit mehreren Benutzern die Ergebnisse in der Tabelle verwenden kann.

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

  6. Temp-Tabelle: Die Temp-Variable kann die Transaktionen nicht verwenden. Wenn wir die DML-Operationen mit der temporären Tabelle ausführen, kann dies ein Rollback sein oder die Transaktionen festschreiben.

    Tabellenvariable: Für Tabellenvariablen ist dies jedoch nicht möglich.

  7. Temp-Tabelle: Funktionen können die Temp-Variable nicht verwenden. Darüber hinaus können wir die DML-Operation in den Funktionen nicht ausführen.

    Tabellenvariable: Mit dieser Funktion können wir jedoch die Tabellenvariable verwenden. Aber mit der Tabellenvariablen können wir das tun.

  8. Temp-Tabelle: Die gespeicherte Prozedur führt die Neukompilierung durch (kann nicht denselben Ausführungsplan verwenden), wenn wir die temporäre Variable für jeden nachfolgenden Aufruf verwenden.

    Tabellenvariable: Während die Tabellenvariable so nicht funktioniert.


8

Für alle, die an den Mythos glauben, dass temporäre Variablen nur im Speicher sind

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


3
Können Sie Ihre Antworten zu einer einzigen Antwort bearbeiten, die die beiden Punkte anspricht?
Joshua Drake

7

Der andere Hauptunterschied besteht darin, dass Tabellenvariablen keine Spaltenstatistik haben, wie dies bei temporären Tabellen der Fall ist. Dies bedeutet, dass der Abfrageoptimierer nicht weiß, wie viele Zeilen sich in der Tabellenvariablen befinden (er schätzt 1), was dazu führen kann, dass höchst nicht optimale Pläne generiert werden, wenn die Tabellenvariable tatsächlich eine große Anzahl von Zeilen enthält.


2
Die rowsSpalte in sys.partitionswird für Tabellenvariablen verwaltet, damit sie tatsächlich weiß, wie viele Zeilen sich in der Tabelle befinden. Dies kann mit gesehen werden OPTION (RECOMPILE). Das Fehlen von Spaltenstatistiken bedeutet jedoch, dass bestimmte Spaltenprädikate nicht geschätzt werden können.
Martin Smith

7

Zitat aus; Professional SQL Server 2012-Interna und Fehlerbehebung

Statistik Der Hauptunterschied zwischen temporären Tabellen und Tabellenvariablen besteht darin, dass keine Statistiken für Tabellenvariablen erstellt werden. Dies hat zwei Hauptfolgen, von denen die erste darin besteht, dass das Abfrageoptimierungsprogramm unabhängig von den darin enthaltenen Daten eine feste Schätzung für die Anzahl der Zeilen in einer Tabellenvariablen verwendet. Darüber hinaus ändert das Hinzufügen oder Entfernen von Daten nichts an der Schätzung.

Indizes Sie können keine Indizes für Tabellenvariablen erstellen, obwohl Sie Einschränkungen erstellen können. Dies bedeutet, dass Sie durch Erstellen von Primärschlüsseln oder eindeutigen Einschränkungen Indizes (da diese zur Unterstützung von Einschränkungen erstellt werden) für Tabellenvariablen haben können. Selbst wenn Sie Einschränkungen und daher Indizes mit Statistiken haben, werden die Indizes beim Kompilieren der Abfrage nicht verwendet, da sie zum Zeitpunkt der Kompilierung nicht vorhanden sind und auch keine Neukompilierungen verursachen.

Schemaänderungen Schemaänderungen sind für temporäre Tabellen möglich, nicht jedoch für Tabellenvariablen. Obwohl Schemaänderungen für temporäre Tabellen möglich sind, vermeiden Sie deren Verwendung, da sie zu einer Neukompilierung von Anweisungen führen, die die Tabellen verwenden.

Temporäre Tabellen versus Tabellenvariablen

TABELLENVARIABLEN WERDEN NICHT IM SPEICHER ERSTELLT

Es gibt ein weit verbreitetes Missverständnis, dass Tabellenvariablen speicherinterne Strukturen sind und daher schneller als temporäre Tabellen arbeiten . Dank einer DMV namens sys. dm _ db _ session _ space _ usage, die die tempdb-Nutzung nach Sitzung anzeigt. Sie können beweisen, dass dies nicht der Fall ist . Führen Sie nach dem Neustart von SQL Server zum Löschen der DMV das folgende Skript aus, um zu bestätigen, dass Ihre Sitzungs-ID 0 für Benutzerobjekte _zuweisung _ Seitenanzahl _ zurückgibt:

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 belegt, indem Sie das folgende Skript ausführen, um eine temporäre Tabelle mit einer Spalte zu erstellen und 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 an, dass der Tabelle eine Seite in Tempdb zugewiesen wurde. Führen Sie nun dasselbe Skript aus, verwenden Sie diesmal jedoch 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 verwenden?

Ob Sie temporäre Tabellen oder Tabellenvariablen verwenden oder nicht, sollte durch gründliche Tests entschieden werden. Es ist jedoch am besten, sich standardmäßig auf temporäre Tabellen zu stützen, da weit weniger Dinge schief gehen können .

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


Tatsächlich werden Statistiken zu Tabellenvariablen erstellt, siehe stackoverflow.com/questions/42824366/…
YuFeng Shen

4

Noch ein Unterschied:

Auf eine Tabelle var 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 Gültigkeitsbereich einer temporären Tabelle umfasst dagegen Code in aufgerufenen Prozeduren und verschachteltem dynamischem SQL.

Wenn auf die von Ihrer Prozedur erstellte Tabelle über andere aufgerufene Prozeduren oder dynamisches SQL zugegriffen werden muss, müssen Sie eine temporäre Tabelle verwenden. Dies kann in komplexen Situationen sehr praktisch sein.


2

Unterschiede zwischen Temporary Tables (##temp/#temp)und Table Variables (@table)sind wie folgt :

  1. Table variable (@table)wird in der erstellt memory. Während a Temporary table (##temp/#temp)in der erstellt wird tempdb database. Bei Speicherdruck können die zu einer Tabellenvariablen gehörenden Seiten jedoch auf tempdb verschoben werden.

  2. Table variableskann nicht beteiligt sein transactions, logging or locking. Das macht @table faster then #temp. Die Tabellenvariable ist also schneller als die temporäre Tabelle.

  3. Temporary tableerlaubt Schemaänderungen im Gegensatz zu Table variables.

  4. Temporary tablessind in der erstellten Routine und auch in den untergeordneten Routinen sichtbar. Während Tabellenvariablen nur in der erstellten Routine sichtbar sind.

  5. Temporary tablessind erlaubt, CREATE INDEXeswährend Table variablesnicht erlaubt sind, CREATE INDEXstattdessen können sie Index mit verwenden Primary Key or Unique Constraint.


1

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


1

Es überrascht mich, dass niemand den Hauptunterschied zwischen diesen beiden erwähnt hat, dass die temporäre Tabelle das parallele Einfügen unterstützt , während die Tabellenvariable dies nicht tut. Sie sollten in der Lage sein, den Unterschied zum Ausführungsplan zu erkennen. Und hier ist das Video von SQL Workshops auf Kanal 9 .

Dies erklärt auch, warum Sie eine temporäre Variable für kleinere Tabellen verwenden sollten, andernfalls eine temporäre Tabelle, wie SQLMenace zuvor geantwortet hat.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.