SQL Server – 7 unterschätzte Features, die jeder DBA und Entwickler kennen sollte

SQL Server – 7 unterschätzte Features
die jeder DBA und Entwickler kennen sollte

📅 14. Mai 2025 ✍️ Erfahrener SQL Entwickler & DBA ⏱ 12 Min. Lesezeit

Einleitung – Jenseits von Indizes und Joins

SQL Server ist vollgepackt mit nützlichen Funktionen, die in vielen Projekten ungenutzt bleiben. Zu oft greifen Entwickler auf umständliche Alternativen zurück, weil sie die elegantere Lösung nicht kennen. In diesem Artikel stelle ich sieben meiner Lieblingsfeatures vor – von der einfachen Paginierung bis hin zur Partitionierung, die das Leben als DBA erheblich erleichtern können.

💡 Hinweis: Die meisten Features gibt es schon seit SQL Server 2012/2016. Prüfen Sie, welche Version Ihre Umgebung nutzt – viele sind auch in Azure SQL Database verfügbar.

1. OFFSET/FETCH – Die moderne Paginierung

Vor SQL Server 2012 musste man auf die teuren ROW_NUMBER()-Konstrukte oder Cursor zurückgreifen, um eine Ergebnismenge zu seitenweise abzufragen. Mit OFFSET/FETCH wird Paginierung zum Kinderspiel – und performanter.

-- 20 Datensätze pro Seite, 3. Seite anzeigen (Seite 1 = OFFSET 0) 
SELECT OrderID, OrderDate, CustomerID 
FROM Sales.Orders 
ORDER BY OrderDate DESC 
OFFSET 40 ROWS -- überspringe die ersten 40 Zeilen 
FETCH NEXT 20 ROWS ONLY; -- dann 20 Zeilen nehmen
💡 Warum wird es zu wenig genutzt? Viele greifen immer noch auf ROW_NUMBER() zurück, weil sie die Syntax nicht kennen. OFFSET/FETCH ist Teil des ANSI SQL Standards und inzwischen auch in PostgreSQL, MySQL (mit LIMIT) verfügbar.

2. STRING_AGG – Strings komfortabel verketten

Früher musste man für die Komma-separierte Liste einen skurrilen STUFF-Trick mit FOR XML PATH anwenden (bitte nicht mehr!). Seit SQL Server 2017 gibt es STRING_AGG.

-- Alle Produktnamen einer Kategorie als Liste 
SELECT CategoryID, 
STRING_AGG(ProductName, ', ') WITHIN GROUP (ORDER BY ProductName) AS ProductList 
FROM Production.Products 
GROUP BY CategoryID;
⚠️ Achtung: Bei sehr langen Strings (über 8 KB) kürzt STRING_AGG still. Dann müssen Sie auf die alte XML-Methode zurückgreifen oder mit NVARCHAR(MAX) arbeiten.

3. CHOOSE & IIF – Kompakte Logik im SELECT

Anstatt ein CASE für einfache Wertauswahlen zu schreiben, kann man CHOOSE (indexbasierte Auswahl) und IIF (if-then-else) nutzen. Die Lesbarkeit steigt enorm.

-- CHOOSE: Quartal aus Monat ableiten 
SELECT OrderID, OrderDate, 
CHOOSE(MONTH(OrderDate), 'Winter','Winter','Frühling','Frühling', 
 'Frühling','Sommer','Sommer','Sommer', 
 'Herbst','Herbst','Herbst','Winter') AS Season 
FROM Sales.Orders; 
 
-- IIF statt CASE für einfache Bedingungen 
SELECT ProductID, UnitPrice, 
IIF(UnitPrice > 100, 'Premium', 'Standard') AS Category 
FROM Production.Products;
💡 Performance: IIF wird intern in ein CASE umgewandelt, es gibt also keinen Geschwindigkeitsverlust. Die Syntax ist jedoch viel kompakter.

4. SEQUENCE – Ordnung ohne IDENTITY

IDENTITY ist toll, aber unflexibel: Sie können keine Zyklen, keine min/max-Werte, keine Caching-Größe einstellen. Mit SEQUENCE (eingeführt in SQL Server 2012) erhalten Sie ein schemaunabhängiges Objekt, das Nummern generiert.

CREATE SEQUENCE dbo.OrderSeq 
AS INT 
START WITH 1 
INCREMENT BY 1 
MINVALUE 1 
MAXVALUE 999999 
CYCLE -- bei MAX wieder von vorne beginnen 
CACHE 50; -- 50 Nummern im Cache (Performance) 
 
-- Nächsten Wert holen 
SELECT NEXT VALUE FOR dbo.OrderSeq; 
 
-- In INSERT verwenden 
INSERT INTO dbo.Orders (OrderID, OrderDate) 
VALUES (NEXT VALUE FOR dbo.OrderSeq, GETDATE());
💡 Vorteil gegenüber IDENTITY: Eine SEQUENCE kann tabellenübergreifend verwendet werden, ermöglicht komplexe Startwerte (z. B. 1000 für jede neue Firma) und ist perfekt für Archivierungsszenarien.

5. CROSS/OUTER APPLY – Funktionen mit Zeilenkontext

APPLY ist nicht neu, aber wird oft übersehen. Es ruft eine tabellenwertige Funktion oder Subquery für jede Zeile der linken Tabelle auf und kann dabei auf die Werte der aktuellen Zeile zugreifen. Das ist extrem nützlich für Top-N-Pro-Zeile oder komplexe Berechnungen.

-- Für jeden Kunden die neueste Bestellung anzeigen 
SELECT c.CustomerID, c.CustomerName, latest.OrderID, latest.OrderDate 
FROM Sales.Customers c 
CROSS APPLY ( 
SELECT TOP 1 OrderID, OrderDate 
FROM Sales.Orders o 
WHERE o.CustomerID = c.CustomerID 
ORDER BY OrderDate DESC 
) AS latest; 
 
-- OUTER APPLY funktioniert wie LEFT JOIN (liefert NULL, wenn keine Zeile)
⚠️ Performance: Bei sehr großen Datenmengen kann APPLY teurer sein als ein Fensterfunktion mit ROW_NUMBER(). Testen Sie beide Varianten.

6. Tabellenwertparameter (TVP) für Bulk-Operationen

Statt viele einzelne INSERT-Befehle zu senden, können Sie eine .NET‑DataTable oder eine JSON-Liste als Tabellenwertparameter übergeben und mit einem einzigen Befehl verarbeiten. Das reduziert Netzwerkroundtrips enorm.

-- Benutzerdefinierter Tabellentyp 
CREATE TYPE dbo.OrderItemType AS TABLE ( 
ProductID INT, Quantity INT, Price DECIMAL(12,2) 
); 
 
-- Prozedur, die TVP entgegennimmt 
CREATE PROCEDURE dbo.InsertOrderItems 
@Items dbo.OrderItemType READONLY 
AS 
BEGIN 
INSERT INTO dbo.OrderDetails (ProductID, Quantity, Price) 
SELECT ProductID, Quantity, Price FROM @Items; 
END;
💡 Warum zu wenig genutzt? Viele Entwickler haben Angst vor dem Aufwand, einen benutzerdefinierten Typ anzulegen. Die Performancegewinne sind aber immens, besonders bei regelmäßigen Updates von tausenden Zeilen.

7. Partitionierte Tabellen & Partition Switching

Das wohl mächtigste, aber am meisten unterschätzte Feature für DBAs. Mit Partitionen können Sie große Tabellen in logische Teile zerlegen. Das Partition Switching erlaubt es, komplette Datenblöcke innerhalb von Millisekunden zu verschieben – z. B. alte Daten in eine Archivtabelle auszulagern.

-- Partitionierte Tabelle erstellen (vereinfacht) 
CREATE PARTITION FUNCTION pf_OrderDate (DATE) 
AS RANGE RIGHT FOR VALUES ('2023-01-01', '2024-01-01', '2025-01-01'); 
 
CREATE PARTITION SCHEME ps_OrderDate 
AS PARTITION pf_OrderDate TO ([PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY]); 
 
CREATE TABLE dbo.OrdersPartitioned ( 
OrderID INT, 
OrderDate DATE NOT NULL, 
Amount DECIMAL(12,2) 
) ON ps_OrderDate(OrderDate); 
 
-- Alte Daten (2022) in eine Archivtabelle auslagern 
CREATE TABLE dbo.OrdersArchive ( -- gleiche Struktur, aber ohne Partitionierung 
OrderID INT, OrderDate DATE, Amount DECIMAL(12,2) 
); 
ALTER TABLE dbo.OrdersPartitioned SWITCH PARTITION 1 TO dbo.OrdersArchive;
⚠️ Aufwand: Die Einrichtung ist etwas komplex, aber einmal konfiguriert sparen Sie enorm viel Zeit beim Datenarchivieren. Nutzen Sie es für sehr große Tabellen (mehrere hundert Millionen Zeilen).

Bonus: Query Store und automatische Optimierung

Der Query Store (seit SQL Server 2016) zeichnet Ausführungspläne, Laufzeiten und Verbrauch auf. Er ist Gold wert für die Plananalyse. In Kombination mit Automatic Tuning (ab 2017) kann SQL Server sogar selbst schlechte Pläne erkennen und zurücknehmen.

-- Query Store aktivieren (pro Datenbank) 
ALTER DATABASE MyDB SET QUERY_STORE = ON; 
 
-- Automatisches Tuning einschalten 
ALTER DATABASE MyDB SET AUTOMATIC_TUNING = ON; 
 
-- Top 5 teuersten Abfragen der letzten Stunde 
SELECT TOP 5 qt.query_sql_text, qs.avg_duration 
FROM sys.query_store_query q 
JOIN sys.query_store_query_text qt ON q.query_text_id = qt.query_text_id 
JOIN sys.query_store_plan p ON q.query_id = p.query_id 
JOIN sys.query_store_runtime_stats qs ON p.plan_id = qs.plan_id 
WHERE qs.last_execution_time > DATEADD(hour, -1, GETDATE()) 
ORDER BY qs.avg_duration DESC;
💡 Mein Tipp: Aktivieren Sie Query Store in jeder Produktivdatenbank. Es hilft nicht nur bei Performanceproblemen, sondern auch bei der Planstabilität nach Upgrades.

Zusammenfassung und Handlungsempfehlung

Viele der hier vorgestellten Features sind keine bahnbrechenden Neuheiten mehr – sie existieren seit Jahren. Dennoch sehen wir sie in Projekten immer wieder ungenutzt. Meine Empfehlung:

  • Für jeden Entwickler: Lernen Sie OFFSET/FETCH, STRING_AGG und APPLY. Sie ersetzen hässliche Workarounds.
  • Für DBAs: Partitionierung und Query Store sollten in Ihrem Werkzeugkasten nicht fehlen.
  • Für Architekten: Tabellenwertparameter und Sequenzen bieten echte Alternativen zu veralteten Patterns.

Nehmen Sie sich eine Stunde Zeit, probieren Sie ein Feature aus, das Sie bisher nicht verwendet haben. Sie werden überrascht sein, wie viel einfacher manche Aufgaben werden.

powered by dtcSoftware