T-SQL Temporäre Tabellen & Tabellenvariablen – Praxisbeispiele

📁 Temporäre Tabellen & Tabellenvariablen in T‑SQL

#temp · ##temp · @table · tempdb · Performance · Best Practices
Lokale und globale temporäre Tabellen sowie Tabellenvariablen verstehen – Geltungsbereiche, Indizierung, Statistik und praktische Entscheidungshilfen.

Grundlagen: Wozu dienen temporäre Objekte?

ÜBERBLICK
Temporäre Tabellen und Tabellenvariablen bieten die Möglichkeit, Zwischenergebnisse innerhalb einer Sitzung, eines Batches oder einer gespeicherten Prozedur abzulegen, ohne permanente Datenbanktabellen zu belasten. Sie werden hauptsächlich in der tempdb gehalten und beim Beenden der Sitzung oder nach Verlassen des Gültigkeitsbereichs automatisch bereinigt. 
-- Vereinfachtes Beispiel: Mehrere Abfragen mit einer temporären Struktur verknüpfen 
CREATE TABLE #Zwischenergebnis (ID INT, Name NVARCHAR(100)); 
INSERT INTO #Zwischenergebnis VALUES (1, 'Datenbank'), (2, 'Entwicklung'); 
SELECT * FROM #Zwischenergebnis; 
-- Die Tabelle existiert nur während der aktuellen Sitzung
💡 Erklärung: Temporäre Strukturen sind essenziell für mehrstufige Prozesse, etwa um aufwendige Joins zu materialisieren, Daten für eine Schleife bereitzustellen oder komplexe Berechnungen in lesbare Schritte zu unterteilen.

Lokale temporäre Tabellen (#temp)

SITZUNGSGEBUNDEN
Lokale temporäre Tabellen werden mit einem einzelnen # erstellt und sind nur für die aktuelle Sitzung (Connection) sichtbar. Sie werden in der tempdb abgelegt und automatisch gelöscht, sobald die Sitzung endet oder der letzte Verweis darauf entfällt. 
-- Schritt 1: Lokale Temp‑Tabelle anlegen und füllen 
CREATE TABLE #KundenBestellungen ( 
KundenID INT, 
Bestellanzahl INT 
); 
INSERT INTO #KundenBestellungen 
SELECT KundenID, COUNT(BestellID) 
FROM Bestellungen 
GROUP BY KundenID; 
 
-- Schritt 2: Mit anderen Tabellen verknüpfen 
SELECT k.Name, kb.Bestellanzahl 
FROM Kunden k 
INNER JOIN #KundenBestellungen kb ON k.KundenID = kb.KundenID; 
 
-- Sauberes Aufräumen (optional, da automatisch) 
DROP TABLE #KundenBestellungen;
🧩 Erklärung: Die Tabelle #KundenBestellungen aggregiert zuerst die Daten aus der Quelltabelle. Durch die anschließende Verknüpfung mit Kunden werden wiederkehrende Aggregationen vermieden. Lokale Temp‑Tabellen unterstützen vollständige Indizierung und Statistiken, was den Optimierer bei großen Datenmengen unterstützt. 

Globale temporäre Tabellen (##temp)

ÜBER SITZUNGEN HINAUS
Globale temporäre Tabellen beginnen mit ## und sind für alle Sitzungen innerhalb der Instanz sichtbar. Sie existieren, bis die erstellende Sitzung endet und keine andere Sitzung mehr auf sie verweist. 
-- Eine Sitzung erstellt die globale Tabelle 
CREATE TABLE ##GlobaleLogTabelle ( 
EreignisID INT, 
Zeitstempel DATETIME DEFAULT GETDATE() 
); 
INSERT INTO ##GlobaleLogTabelle (EreignisID) VALUES (1), (2); 
 
-- Eine andere Sitzung kann auf die Tabelle zugreifen 
SELECT * FROM ##GlobaleLogTabelle; 
 
-- Explizites Löschen beendet die Lebensdauer sofort 
DROP TABLE ##GlobaleLogTabelle;
🌐 Erklärung: Globale Temp‑Tabellen eignen sich für szenarioübergreifende Prozesse, z. B. wenn mehrere parallele Jobs eine gemeinsame Arbeitsfläche benötigen. Wegen des gemeinsamen Zugriffs ist jedoch ein versehentliches Überschreiben möglich – daher mit Bedacht einsetzen.

Tabellenvariablen (@table)

SCOPE · BATCH
Tabellenvariablen werden mit DECLARE @variable TABLE(...) deklariert. Sie existieren nur innerhalb des aktuellen Batches, einer Funktion oder einer gespeicherten Prozedur. Ihr Geltungsbereich endet, sobald die umschließende Routine oder Batch beendet wird. 
DECLARE @MeineTabelle TABLE ( 
ArtikelID INT PRIMARY KEY, 
Menge INT, 
Status NVARCHAR(20) 
); 
 
INSERT INTO @MeineTabelle VALUES (101, 5, 'offen'), (102, 3, 'versendet'); 
 
SELECT * FROM @MeineTabelle; 
 
-- Nach Ende des Batches ist die Variable automatisch aufgeräumt
🔒 Erklärung: Tabellenvariablen verursachen weniger Neukompilierungen als temporäre Tabellen und greifen auf keine Transaktionsprotokolle zurück. Sie eignen sich besonders für kleine, nicht persistente Zwischenergebnisse (wenige hundert Zeilen). Allerdings fehlen ihnen Statistiken, was bei größeren Datenmengen zu ungünstigen Ausführungsplänen führen kann. 
🚀 Optimierungstipp: Ein Primärschlüssel auf einer Tabellenvariable erzeugt einen gruppierten Index – das verbessert Zugriffe auf bestimmte Zeilen deutlich. 

Vergleich: #temp vs. ##temp vs. @table

PERFORMANCE & MERKMALE
Die folgende Tabelle fasst die wesentlichen Unterschiede zusammen – von Gültigkeitsbereich über Indizierungsmöglichkeiten bis hin zum Verhalten im Transaktionskontext. Diese Entscheidungshilfe erleichtert die Wahl des passenden Temporärobjekts.
/* --- 1. Indizierung und Statistiken --- */ 
-- Temporäre Tabelle: volle Indexunterstützung + Statistiken 
CREATE TABLE #BeispielTemp (ID INT PRIMARY KEY, Wert NVARCHAR(100)); 
CREATE INDEX IX_Wert ON #BeispielTemp(Wert); 
 
-- Tabellenvariable: nur PRIMARY KEY / UNIQUE (in neueren Versionen auch Non‑Clustered) 
DECLARE @BeispielVar TABLE (ID INT PRIMARY KEY, Wert NVARCHAR(100)); 
 
/* --- 2. Transaktionsverhalten --- */ 
BEGIN TRAN; 
INSERT INTO #BeispielTemp VALUES (1, 'Temp'); 
INSERT INTO @BeispielVar VALUES (1, 'Var'); 
ROLLBACK; 
-- Ergebnis: #BeispielTemp wird zurückgesetzt, @BeispielVar bleibt gefüllt. 
 
/* --- 3. Geltungsbereich und Sichtbarkeit --- */ 
-- #temp: nur aktuelle Sitzung (einschließlich verschachtelter SPs) 
-- ##temp: alle Sitzungen (bis letzter Verweis verschwindet) 
-- @table: nur aktueller Batch / aktuelle Routine
⚖️ Zusammenfassung:
  • Lokale Temp‑Tabelle (#): Volle Index- und Statistikunterstützung, sichtbar in der gesamten Sitzung, ideal für mittlere bis große Datenmengen oder komplexe Abfragen. 
  • Globale Temp‑Tabelle (##): Wie #, aber für mehrere Sitzungen sichtbar – nützlich für abteilungsübergreifende Arbeitsflächen.
  • Tabellenvariable (@table): Sehr geringer Overhead, keine Statistik, kein Transaktions‑Rollback – perfekt für kleine Lookuptabellen oder temporäre Parameter in Funktionen. 
Ein einfaches Kriterium: Liegt die zu erwartende Zeilenzahl unter etwa 5000 und sind keine aufwendigen Joins nötig, spricht viel für eine Tabellenvariable. Bei großen Datenmengen oder der Notwendigkeit von Statistiken sind temporäre Tabellen die bessere Wahl. 

Best Practices & häufige Fallstricke

PRAXISTIPPS
Auch wenn temporäre Strukturen einfach erscheinen, gibt es einige Details, die vor Überraschungen schützen und die Leistung verbessern.
-- ✅ Vor der Erstellung eine vorhandene Temp‑Tabelle löschen (robust) 
IF OBJECT_ID('tempdb..#Sicherheit', 'U') IS NOT NULL 
DROP TABLE #Sicherheit; 
 
CREATE TABLE #Sicherheit (ID INT); 
 
-- ❌ Vermeiden: Tabellenvariable in dynamischem SQL (außerhalb des Scopes) 
DECLARE @sql NVARCHAR(MAX) = 'SELECT * FROM @MeineTabelle'; 
EXEC sp_executesql @sql; -- Fehler: @MeineTabelle nicht bekannt 
 
-- ✅ Aber: Temporäre Tabelle funktioniert im dynamischen SQL 
EXEC ('CREATE TABLE #TempDyn (ID INT); INSERT INTO #TempDyn VALUES (1); SELECT * FROM #TempDyn;'); 
 
-- 💡 Index auf Tabellenvariablen explizit vorgeben (ab SQL Server 2014) 
DECLARE @Optimiert TABLE ( 
Id INT PRIMARY KEY, 
Suchwert NVARCHAR(100) INDEX IX_Suchwert NONCLUSTERED 
);
🧠 Entscheidungshilfe – wann was nutzen?
  • Singleton-Lookups oder sehr kleine Ergebnismengen: Tabellenvariable
  • Permanente Indexierung, große Datenmengen oder parallele Pläne gewünscht: Lokale Temp‑Tabelle
  • Datenaustausch zwischen verschiedenen Sitzungen: Globale Temp‑Tabelle (##temp)
  • Fehlerprotokollierung, die ein ROLLBACK überstehen muss: Tabellenvariable (bleibt bei Rücksetzung erhalten)
📌 Zusammenfassende Tipps:
  • Verwenden Sie SELECT ... INTO #temp, um schnell eine Temp‑Tabelle aus einem Abfrageergebnis zu erzeugen.
  • Beachten Sie, dass lokale Temp‑Tabellen in der tempdb ähnlich viel Logging verursachen wie normale Tabellen, während Tabellenvariablen dies minimieren.
  • Der Geltungsbereich einer Tabellenvariablen endet mit dem Batch – sie ist nicht in verschachtelten gespeicherten Prozeduren sichtbar. 
  • Setzen Sie primäre und ggf. nicht gruppierte Indizes auf Tabellenvariablen, um Suchoperationen zu beschleunigen.
  • Nutzen Sie die tempdb‑Leistungsindikatoren, um Engpässe durch übermäßige Temp‑Tabellen‑Aktivität zu erkennen.

📄 Spickzettel: Syntax auf einen Blick

-- Lokale temporäre Tabelle (existiert nur in der aktuellen Sitzung) 
CREATE TABLE #LokalTemp (Spalte1 INT, Spalte2 NVARCHAR(50)); 
-- einfache Befüllung 
INSERT INTO #LokalTemp SELECT ID, Name FROM Quelle; 
 
-- Globale temporäre Tabelle (sichtbar für alle Sitzungen) 
CREATE TABLE ##GlobalTemp (ID INT PRIMARY KEY, Wert DECIMAL(10,2)); 
 
-- Tabellenvariable (nur im aktuellen Batch gültig) 
DECLARE @TabVar TABLE (ID INT PRIMARY KEY, Beschreibung NVARCHAR(200)); 
INSERT INTO @TabVar VALUES (1, 'Beispiel'); 
SELECT * FROM @TabVar;

Fazit – Temporäre Tabellen und Tabellenvariablen sind unverzichtbare Werkzeuge für saubere, mehrstufige T‑SQL‑Logik. Mit dem Wissen über Lebensdauer, Indizierung und Performanceeigenschaften können Sie die passende Struktur für Ihre Arbeitslast auswählen.