SQL Server – 7 unterschätzte Features
die jeder DBA und Entwickler kennen sollte
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.
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
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;
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;
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());
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)
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;
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;
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;
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