Seit Jahrzehnten stehe ich irgendwie auf Kriegsfuß mit SQL. So eine Art Hassliebe. Ich brauche SQL sehr häufig und finde die Syntax trotzdem furchtbar.
JOINs habe ich inzwischen relativ gut verstanden, glaube ich, um die PIVOT-Funktionalität habe ich bisher einen Bogen gemacht.
Doch aktuell war das nicht mehr möglich. Deswegen habe ich mir zahlreiche Ressourcen angeschaut.
Ressourcen/Tutorials
- https://www.c-sharpcorner.com/UploadFile/f0b2ed/pivot-and-unpovit-in-sql-server/
- https://dev.to/erikwhiting88/simple-sql-tutorial-how-to-pivot-2cjl
- https://www.sqlservertutorial.net/sql-server-basics/sql-server-pivot/
- https://www.sqlservertutorial.org/sql-server-pivot/
- https://docs.microsoft.com/de-de/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-ver15
- https://mode.com/sql-tutorial/sql-pivot-table
- https://www.codeproject.com/Tips/500811/Simple-Way-To-Use-Pivot-In-SQL-Query
- https://riptutorial.com/sql-server/example/8325/simple-pivot---unpivot--t-sql-
- https://www.javatpoint.com/t-sql-pivot-and-unpivot
- https://www.sqlshack.com/dynamic-pivot-tables-in-sql-server/
- https://www.kodyaz.com/articles/t-sql-pivot-tables-in-sql-server-tutorial-with-examples.aspx
Das oberste Tutorial war für mich das Verständlichste.
Im Grunde ist PIVOT einfach ein 90-Grad-Rotieren von Zeilen in Spalten.
Eigenes Beispielprojekt
Ich habe mir eine neue SQL-Server-Datenbank mit genau zwei Tabellen erstellt, die mein „echtes“ Problem so weit vereinfachen, dass ich damit die Lösung isoliert angehen und lösen kann.
Also quasi das klassische „Minimal, Reproducible Example“.
Struktur
Es gibt eine Kopfdatensatz-Tabelle MyForm:
CREATE TABLE [MyForm](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](250) NOT NULL,
[DateCreated] [datetime] NOT NULL)
Und es gibt eine Detaildatensatz-Tabelle MyFormField mit Name-Werte-Paaren:
CREATE TABLE [MyFormField](
[Id] [int] IDENTITY(1,1) NOT NULL,
[MyFormId] [int] NOT NULL,
[FieldName] [nvarchar](250) NOT NULL,
[FieldValue] [nvarchar](max) NULL)
sowie dem Fremdschlüssel:
ALTER TABLE [MyFormField]
WITH CHECK ADD CONSTRAINT [FK_MyFormField_MyForm]
FOREIGN KEY([MyFormId])
REFERENCES [MyForm] ([Id])
Grafisch schaut das so aus:

Daten
Die Tabelle MyForm ist so gefüllt:

Und die Tabelle MyFormField ist so gefüllt:

Pivot-Abfrage
SELECT
-- Die Kopfdatensatz-Spalten holen.
FormId, FormName,
-- Die Detaildatensatz-Spalten als Pivots holen.
[Field A], [Field B], [Field C], [Field D]
FROM
(
-- Hier die Kopfdaten und die Detaildatensätze redundant
-- zusammen joinen, damit wir aus das gesamte pivoten können.
SELECT
MyForm.Id AS FormId,
MyForm.Name AS FormName,
MyFormField.FieldName,
MyFormField.FieldValue
FROM
MyForm LEFT OUTER JOIN
MyFormField ON MyForm.Id = MyFormField.MyFormId
) t
PIVOT
(
-- Irgendwas aus der Detailtabelle als Aggregatsfunktion.
MAX(FieldValue)
-- Die Zeilen als Spalten transponieren.
FOR FieldName IN ([Field A], [Field B], [Field C], [Field D])
) as pvt
Ergebnis
Die obige Abfrage hat dann dieses Ergebnis geliefert:

Das kann ich dann entsprechend adaptieren auf mein „echtes“ Projekt.