SQL Server PIVOT und UNPIVOT – Daten drehen leicht gemacht

SQL Server PIVOT & UNPIVOT – Daten drehen leicht gemacht

📅 14. Mai 2025 ✍️ Von einem erfahrenen SQL Entwickler & DBA ⏱ 8 Min. Lesezeit

Einleitung: Was ist PIVOT und UNPIVOT?

Im täglichen Reporting stoßen wir häufig auf das Bedürfnis, Daten von Zeilen in Spalten umzudrehen (PIVOT) oder mehrere Spalten in Zeilen zu überführen (UNPIVOT). SQL Server bietet hierfür die Operatoren PIVOT und UNPIVOT – relationale Operatoren, die seit SQL Server 2005 verfügbar sind. In diesem Artikel erkläre ich anhand praktischer Beispiele, wie Sie sie effektiv einsetzen, welche Fallstricke es gibt und wann Alternativen wie die klassische CASE-basierten Aggregationen oder dynamisches SQL besser geeignet sind.

📊 1. PIVOT – Aus Zeilen werden Spalten

Nehmen wir eine typische Verkaufstabelle mit monatlichen Umsätzen. Oft möchten Sie die Monate als Spalten darstellen. PIVOT erledigt dies mit einer übersichtlichen Syntax.

-- Beispieldaten: Verkäufe pro Monat und Produktkategorie 
CREATE TABLE #Sales (Category VARCHAR(20), MonthName VARCHAR(10), Amount DECIMAL(10,2)); 
INSERT INTO #Sales VALUES 
('Elektronik', 'Jan', 1500), ('Elektronik', 'Feb', 2200), ('Elektronik', 'Mär', 1800), 
('Bekleidung', 'Jan', 800), ('Bekleidung', 'Feb', 1200), ('Bekleidung', 'Mär', 950); 
 
-- PIVOT: Jeder Monat wird eine Spalte 
SELECT Category, Jan, Feb, Mär 
FROM ( 
SELECT Category, MonthName, Amount FROM #Sales 
) AS SourceTable 
PIVOT ( 
SUM(Amount) FOR MonthName IN ([Jan], [Feb], [Mär]) 
) AS PivotTable;
💡 Wichtig: Die PIVOT-Klausel benötigt eine Aggregationsfunktion (SUM, AVG, COUNT, ...) und die Liste der Zielspalten in eckigen Klammern. Die Werte in der Spalte, über die gepivottet wird (MonthName), werden zu Spaltennamen.

Ergebnis:

Category Jan Feb Mär 
Elektronik 1500 2200 1800 
Bekleidung 800 1200 950

Alle nicht genannten Spalten (hier Category) bleiben als Zeilen gruppiert erhalten – ähnlich wie bei GROUP BY.

🔄 2. UNPIVOT – Aus Spalten werden Zeilen

UNPIVOT ist der Gegenoperator: Sie haben bereits eine breite Tabelle (z. B. mit monatlichen Spalten) und möchten diese normalisieren. UNPIVOT wandelt mehrere Spalten in Zeilenpaare um.

-- Breite Tabelle (Ergebnis eines PIVOT) 
CREATE TABLE #SalesPivot (Category VARCHAR(20), Jan DECIMAL(10,2), Feb DECIMAL(10,2), Mär DECIMAL(10,2)); 
INSERT INTO #SalesPivot VALUES 
('Elektronik', 1500, 2200, 1800), 
('Bekleidung', 800, 1200, 950); 
 
-- UNPIVOT: Spalten Jan, Feb, Mär in Zeilen umwandeln 
SELECT Category, MonthName, Amount 
FROM #SalesPivot 
UNPIVOT ( 
Amount FOR MonthName IN (Jan, Feb, Mär) 
) AS unpvt;

UNPIVOT erzeugt für jede angegebene Spalte (Jan, Feb, Mär) eine Zeile, in der MonthName den ursprünglichen Spaltennamen und Amount den Wert enthält. NULL-Werte werden automatisch ignoriert (die Zeile entfällt).

⚠️ Hinweis: UNPIVOT kann nicht mit dynamischen Spaltenlisten arbeiten – Sie müssen alle Zielspalten explizit auflisten. Bei vielen Spalten ist dynamisches SQL eine Alternative.

🧠 3. PIVOT mit mehreren Aggregationen & dynamischen Spalten

Oft benötigen Sie nicht nur eine Aggregation, sondern z. B. gleichzeitig Summe und Anzahl. PIVOT erlaubt nur eine Aggregatsfunktion – für mehrere müssen Sie auf die klassische CASE-Methode ausweichen oder zweimal pivottieren und die Ergebnisse joinen.

Beispiel mit CASE (flexibel, auch für dynamische Spalten geeignet):

SELECT Category, 
SUM(CASE WHEN MonthName = 'Jan' THEN Amount END) AS Jan, 
SUM(CASE WHEN MonthName = 'Feb' THEN Amount END) AS Feb 
FROM #Sales 
GROUP BY Category;
💡 Dynamisches PIVOT: Wenn die Werte, die zu Spalten werden sollen, nicht feststehen (z. B. Monatsnamen aus der Tabelle), müssen Sie dynamisches SQL verwenden, um die Spaltenliste zu erzeugen. Das ist aufwendiger, aber die einzige Möglichkeit für voll flexible Berichte.

⚠️ 4. Fallstricke und Besonderheiten

  • Datentypen: PIVOT verwendet implizite Konvertierungen. Stellen Sie sicher, dass die gepivotete Spalte und die Zielspalten kompatible Datentypen haben. Bei UNPIVOT müssen alle aufgelisteten Spalten denselben Datentyp haben – sonst erhalten Sie einen Fehler (verwenden Sie CAST/CONVERT).
  • NULL-Werte: PIVOT ignoriert Zeilen, in denen die gepivotete Spalte NULL ist? Nein – die Zeile taucht auf, aber der aggregierte Wert kann NULL sein. UNPIVOT ignoriert NULL-Quellwerte – die resultierende Zeile wird nicht erzeugt.
  • Sortierung und Spaltennamen: Spaltennamen im PIVOT werden zu Bezeichnern. Verwenden Sie bei gemischten Groß-/Kleinschreibung oder Sonderzeichen eckige Klammern.
  • Leistung: PIVOT ist oft gut optimiert, kann aber bei sehr vielen Spalten zu komplexen Plänen führen. Die CASE-Methode skaliert oft besser, wenn viele Zeilen aggregiert werden müssen.

📐 5. Praktisches Beispiel: Quartalsumsätze pro Produkt

Ein realistischeres Szenario: Sie möchten monatliche Verkaufszahlen in Quartalsspalten gruppieren.

WITH MonthlySales AS ( 
SELECT ProductID, 
DATEPART(QUARTER, OrderDate) AS Quarter, 
SUM(Amount) AS TotalAmount 
FROM Sales 
GROUP BY ProductID, DATEPART(QUARTER, OrderDate) 
) 
SELECT ProductID, [1] AS Q1, [2] AS Q2, [3] AS Q3, [4] AS Q4 
FROM MonthlySales 
PIVOT ( 
SUM(TotalAmount) FOR Quarter IN ([1],[2],[3],[4]) 
) AS PivotQuarter;
💡 Hinweis: Die CTE aggregiert vor dem Pivot – das ist oft effizienter, als direkt auf die Basistabelle zu pivottieren.

🛠️ 6. Dynamisches PIVOT (wenn die Werte nicht feststehen)

Manchmal kennen Sie die Werte, die zu Spalten werden sollen, zur Entwicklungszeit nicht (z. B. verschiedene Produktnamen). Dann hilft dynamisches SQL:

DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX); 
 
-- Spaltenliste dynamisch aus Tabelle erzeugen 
SELECT @columns = STRING_AGG(QUOTENAME(MonthName), ', ') 
FROM (SELECT DISTINCT MonthName FROM #Sales) AS Months; 
 
SET @sql = ' 
SELECT Category, ' + @columns + ' 
FROM ( 
SELECT Category, MonthName, Amount FROM #Sales 
) AS src 
PIVOT ( 
SUM(Amount) FOR MonthName IN (' + @columns + ') 
) AS pvt'; 
 
EXEC sp_executesql @sql;
⚠️ Sicherheitshinweis: Dynamisches PIVOT birgt Risiken (SQL Injection), wenn die Werte aus Benutzereingaben stammen. Verwenden Sie QUOTENAME für die Spaltennamen und validieren Sie die Quelltabelle.

📋 7. Alternativen und wann man sie einsetzt

  • Klassische Fallunterscheidung (CASE): Flexibler, erlaubt mehrere Aggregate und ist einfacher zu lesen, wenn die Spalten feststehen. Oft performanter als PIVOT bei großen Datenmengen.
  • STRING_AGG + JSON / XML: Für sehr dynamische Spalten oder wenn Sie die Ergebnisse direkt in einem anderen Format benötigen.
  • Power Query / Berichtstools: Manchmal ist es besser, die Transformation im Reporting-Tool vorzunehmen, um die Datenbank zu entlasten.
💡 Meine Empfehlung: Verwenden Sie PIVOT/UNPIVOT für klare, feste Spalten – vor allem in Ad‑hoc‑Abfragen und wenn die Lesbarkeit wichtig ist. Für komplexe oder variable Anforderungen sind CASE oder dynamisches SQL geeigneter.

🎯 Fazit

PIVOT und UNPIVOT sind mächtige Werkzeuge, um Daten zwischen Zeilen- und Spaltenorientierung zu konvertieren. Sie machen Abfragen kompakter und lesbarer, haben aber ihre Tücken (feste Spaltenliste, nur ein Aggregat). In der Praxis kombiniere ich sie oft mit CTEs, dynamischem SQL oder weiche auf die bewährte CASE-Methode aus. Entscheidend ist, die Anforderungen zu verstehen: Handelt es sich um einen festen Bericht oder ein flexibles Dashboard? Für periodische, feste Reports (Monate, Quartale) ist PIVOT ideal – für frei wählbare Dimensionen greifen Sie zu dynamischen Lösungen.

powered by dtcSoftware