SQL-Tricks: 7 fortgeschrittene Techniken für MS SQL Server (T-SQL)

⚡ 7 SQL-Tricks für MS SQL Server

Deklarativ, mächtig, unerwartet – fortgeschrittene T-SQL Techniken, die jede Datenbank-Arbeit revolutionieren.
inspiriert von Lukas Eders berühmter Vortragsreihe, umgesetzt für Microsoft T-SQL.
🎯 MS SQL Server · T-SQL

Alles ist eine Tabelle, rekursive CTEs, Fensterfunktionen, Lückenanalyse und mehr – diese sieben Techniken zeigen, wie weit man mit reinem deklarativem SQL gehen kann. Fokus liegt auf MS SQL Server 2017+ (kompatibel mit neueren Versionen). Keine imperative Schleifenlogik, dafür elegante, lesbare Abfragen.

01 📋 Alles ist eine Tabelle
Jedes SELECT-Statement liefert eine Ergebnismenge (Tabelle). Auch Konstanten, VALUES-Konstrukt oder Unterabfragen sind Tabellen – das vereinheitlicht die gesamte SQL-Logik.
-- Werte als Tabelle erzeugen (MS SQL Server) 
SELECT * 
FROM (VALUES (1), (2), (3)) AS t(a); 
 
-- INSERT als "INSERT ... SELECT ..." (Tabellen einfügen) 
CREATE TABLE #temp (zahl INT); 
INSERT INTO #temp (zahl) 
SELECT v.a 
FROM (VALUES (10),(20),(30)) v(a); 
 
SELECT * FROM #temp; 
DROP TABLE #temp;
🔍 In MS SQL kann jede Abfrage als „Tabelle“ in einer FROM-Klausel verwendet werden. Das macht Unterabfragen, CTEs und VALUES-Konstrukte extrem flexibel.
02 🔄 Datengenerierung mit rekursivem CTE
Mit Common Table Expressions (WITH RECURSIVE – SQL Standard) lassen sich Hierarchien, Sequenzen oder sogar die Mandelbrot-Menge generieren.
-- Zahlen von 1 bis 10 rekursiv erzeugen (MS SQL Server) 
WITH Rekursion AS ( 
SELECT 1 AS Zahl -- Anker / Seed 
UNION ALL 
SELECT Zahl + 1 
FROM Rekursion 
WHERE Zahl < 10 
) 
SELECT * FROM Rekursion 
OPTION (MAXRECURSION 100);
🧠 Die obere Abfrage ist ein rekursiver CTE – jede Iteration addiert 1. Perfekt für Kalendertabellen, Pfad-Aggregationen oder Zerlegung von Zeichenketten in T-SQL.
03 📊 Laufende Summe & Fensterfunktionen
Mit SUM() OVER() kannst du kumulative Summen, gleitende Durchschnitte oder Rangfolgen ohne Selbst-Joins berechnen – hochperformant.
-- Laufende Summe (running total) pro Abteilung 
SELECT  
Abteilung, 
Mitarbeiter, 
Gehalt, 
SUM(Gehalt) OVER (PARTITION BY Abteilung ORDER BY Mitarbeiter  
ROWS UNBOUNDED PRECEDING) AS Laufende_Summe 
FROM Angestellte;
📈 Fensterfunktionen (OVER-Klausel) sind in MS SQL Server vollständig implementiert. Sie ersetzen teure Korrelationen und ermöglichen komplexe Business Analytics in einem einzigen Durchlauf.
04 📅 Längste, lückenlose Abfolge (Gaps & Islands)
Ermitteln der maximalen Anzahl aufeinanderfolgender Tage / IDs – z.B. für "Login-Streaks" oder Verfügbarkeitszeiträume.
-- Beispiel: Login-Daten eines Users – längste Serie von aufeinanderfolgenden Tagen 
WITH LoginDaten AS ( 
SELECT DISTINCT CAST(LoginZeit AS DATE) AS LoginDatum 
FROM BenutzerLogins 
WHERE BenutzerID = 123 
), 
Gruppiert AS ( 
SELECT LoginDatum, 
ROW_NUMBER() OVER (ORDER BY LoginDatum) AS rn 
FROM LoginDaten 
), 
Gruppen AS ( 
SELECT LoginDatum, 
DATEADD(day, -rn, LoginDatum) AS Gruppe 
FROM Gruppiert 
) 
SELECT  
MIN(LoginDatum) AS Start, 
MAX(LoginDatum) AS Ende, 
COUNT(*) AS Tage 
FROM Gruppen 
GROUP BY Gruppe 
ORDER BY Tage DESC;
⭐ Der Trick: Zeilennummer abziehen, um zusammenhängende Tage zu gruppieren. Bekannt als "Tabibitosan-Methode". Funktioniert mit jedem Datentyp, der eine natürliche Ordnung hat.
05 📏 Länge von Abfolgen: LEAD() & LAG()
Mit den Fensterfunktionen LEAD und LAG kann man Änderungen eines Werts erkennen und zusammenhängende Blöcke zählen.
-- Gruppieren von aufeinanderfolgenden Zeilen mit gleichem Vorzeichen (positive/negative Beträge) 
WITH DatenMitRN AS ( 
SELECT ID, Betrag, 
SIGN(Betrag) AS Vorzeichen, 
ROW_NUMBER() OVER (ORDER BY ID) AS rn 
FROM Transaktionen 
), 
Grenzen AS ( 
SELECT *, 
CASE WHEN LAG(Vorzeichen) OVER (ORDER BY ID) != Vorzeichen  
THEN rn END AS StartBlock, 
CASE WHEN LEAD(Vorzeichen) OVER (ORDER BY ID) != Vorzeichen  
THEN rn END AS EndBlock 
FROM DatenMitRN 
), 
Bloecke AS ( 
SELECT ID, Betrag, Vorzeichen, 
MAX(StartBlock) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING) AS BlockStart, 
MIN(EndBlock) OVER (ORDER BY ID ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS BlockEnd 
FROM Grenzen 
) 
SELECT *, (BlockEnd - BlockStart + 1) AS BlockLaenge 
FROM Bloecke;
✍️ LAG prüft den Vorgänger, LEAD den Nachfolger. Zusammen mit IGNORE NULLS (oder geschicktem Coalesce) lassen sich beliebige Folgen von Datensätzen segmentieren – nützlich für Aktienkursbewegungen oder Bestandsänderungen.
06 🧩 Teilsummenproblem (Subset Sum) mit CTE
Klassisches NP-Problem – aber mit rekursivem SQL und dynamischer Programmierung möglich. Für kleinere Datenmengen enorm praktisch.
-- Finde alle möglichen Summen aus einer Liste von Zahlen (2^n Kombinationen) 
WITH Items AS ( 
SELECT 1 AS ID, 7120 AS Wert UNION ALL 
SELECT 2, 8150 UNION ALL SELECT 3, 8255 UNION ALL SELECT 4, 9051 
), 
RekSum AS ( 
SELECT Wert, CAST(Wert AS VARCHAR(MAX)) AS Pfad, ID 
FROM Items 
UNION ALL 
SELECT r.Wert + i.Wert, r.Pfad + ' + ' + CAST(i.Wert AS VARCHAR), i.ID 
FROM RekSum r 
JOIN Items i ON r.ID < i.ID 
) 
SELECT Wert, Pfad  
FROM RekSum 
ORDER BY Wert;
⚙️ Die Abfrage baut kombinatorische Summen rekursiv auf. In MS SQL Server kann man damit Budget-Analysen oder "beste Passung" für Materialbedarfsplanung durchführen.
07 ✂️ Gekapptes Running Total (nie unter 0)
Laufende Summe, die niemals negativ wird – z.B. für Lagerbestände oder Kontostand mit Untergrenze 0.
-- T-SQL Simulation eines "Running Total with Floor" mittels Fensterfunktion & Korrektur 
WITH Trans AS ( 
SELECT Datum, Betrag, 
SUM(Betrag) OVER (ORDER BY Datum ROWS UNBOUNDED PRECEDING) AS StandardTotal 
FROM KontoBewegungen 
) 
SELECT Datum, Betrag, 
CASE  
WHEN StandardTotal < 0 THEN 0  
ELSE StandardTotal  
END AS TotalGekappt 
FROM Trans; 
 
-- Bessere Variante für echten "Floor(0)" rekursiv (tatsächliches Abschneiden) 
-- (Hier als rekursiver CTE mit IIF LIMIT 0) 
WITH Bestand AS ( 
SELECT Datum, Betrag,  
IIF(Betrag < 0, 0, Betrag) AS BestandNeu, 
1 AS Reihenfolge 
FROM KontoBewegungen 
WHERE Reihenfolge = 1 
 
UNION ALL 
 
SELECT b2.Datum, b2.Betrag, 
IIF(b.BestandNeu + b2.Betrag < 0, 0, b.BestandNeu + b2.Betrag), 
b.Reihenfolge + 1 
FROM Bestand b 
JOIN KontoBewegungen b2 ON b2.Reihenfolge = b.Reihenfolge + 1 
) 
SELECT * FROM Bestand 
OPTION (MAXRECURSION 0);
📉 Die erste Option zeigt eine einfache Kappung, die zweite rekursive Variante bildet das echte „nicht-negativ“-Verhalten ab (wie in Oracle mit MODEL). Für MS SQL Server kann man Window-Aggregate mit rekursiver Logik mischen, um das exakte Verhalten zu erhalten.