SQL-Indizes
Acht Kostbarkeiten in T-SQL, Teil 2
In der SQL-Küche ist der Index das Salz in der Suppe: Zu wenig, und jede Abfrage schmeckt fad und läuft träge; zu viel, und das System wird schwer verdaulich. Indizes sind eines der mächtigsten, aber auch missverstandenen Werkzeuge im SQL Server. Richtig eingesetzt bringen sie enorme Performancegewinne – falsch dosiert verursachen sie Wartungsaufwand, Speicherlast und Frust. Dieser Artikel zeigt, welche Arten von Indizes es gibt, wie sie funktionieren und wie Entwickler wie Administratoren gemeinsam das richtige Maß finden.
Welche Arten von Indizes werden betrachtet?
Dieser Artikel beleuchtet die wichtigsten Indexarten und ihren Beitrag zur Performance (Bild 1). Im Mittelpunkt stehen der Clustered, Nonclustered und Filtered Index. Es geht nicht um Indizes für XML, JSON, Vektoren oder räumliche Daten. Auch findet der Columnstore Index keine Erwähnung.
Unterschiedliche Arten von Indizes (Bild 1)
AutorWas macht ein Index eigentlich?
Ein Index ist eine strukturierte Datenablage, die Abfragen beschleunigt – ähnlich einem Inhaltsverzeichnis in einem Buch. Ohne Index muss SQL Server jede Seite durchsuchen (Full Table Scan). Mit Index findet der Server die gesuchten Zeilen gezielt über Schlüsselwerte. Das Grundprinzip basiert auf einer B-Baumstruktur (Balanced Tree): Jeder Knoten verweist auf untergeordnete Seiten, bis schließlich die eigentlichen Datenzeilen erreicht werden. Diese Struktur erlaubt schnelle Suchen, Einfügungen und Bereichsabfragen.
Indizes kosten jedoch Platz und Pflege: Bei jedem INSERT, UPDATE oder DELETE müssen sie aktualisiert werden. Mehr Indizes bedeuten also mehr Schreiblast. Die Kunst liegt darin, das richtige Gleichgewicht zwischen Lesegeschwindigkeit und Wartungsaufwand zu finden.
Clustered Index – das Rückgrat der Tabelle
Jede Tabelle kann genau einen Clustered Index besitzen. Er bestimmt die physische Sortierung der Daten und definiert die logische Reihenfolge der Datenseiten. Eine Tabelle ohne Clustered Index ist ein Heap – schnell für Masseninserts, aber oft ineffizient bei gezielten Abfragen. In der Regel wählt man als Clustered Key eine Spalte mit eindeutigem, monoton wachsendem Wert – typischerweise eine IDENTITY oder GUIDs. Und meistens ist das dann auch gleich der Primary Key der Tabelle. Beispiel:
CREATE CLUSTERED INDEX IX_Sales_OrderId ON Sales.Orders(OrderId);
Ein guter Clustered Key ist kompakt, eindeutig und unveränderlich. Schlechte Schlüssel wie Textspalten oder GUIDs ohne NEWSEQUENTIALID() führen zu Fragmentierung – die Seiten im B-Baum müssen ständig umsortiert werden (Page Splits). Das belastet I/O und reduziert die Cache-Effizienz.
Nonclustered Index – die Extrabeilage
Ein Nonclustered Index enthält Kopien ausgewählter Spalten, sortiert nach dem Indexschlüssel, plus einen Zeiger auf die tatsächliche Datenzeile. Er ist optimal für häufige Filter- oder Join-Spalten, die nicht Teil des Clustered Keys sind (Bild 2).
Clustered Index versus Nonclustered Index on Heap (Bild 2)
AutorEine Tabelle kann mehrere, genauer gesagt bis zu 999, solcher Indizes erhalten. Beispiel:
CREATE NONCLUSTERED INDEX IX_Customer_LastName ON Sales.Customers(LastName);
Wird eine Abfrage ausgeführt, deren benötigte Spalten vollständig im Index enthalten sind, kann SQL Server sie direkt daraus bedienen – man spricht von einer „Covering Query“. Mit INCLUDE-Spalten lässt sich das gezielt erreichen:
CREATE NONCLUSTERED INDEX IX_Orders_CustomerId_Includes ON Sales.Orders(CustomerId) INCLUDE(OrderDate, Amount);
Seit SQL Server 2008 gibt es Filtered Indizes – sie enthalten nur Zeilen, die bestimmte Kriterien erfüllen. Das spart Speicherplatz und reduziert Wartungsaufwand – ideal bei stark selektiven Abfragen. Beispiel:
CREATE INDEX IX_OpenOrders ON Sales.Orders(Status) WHERE Status = 'Open';
Solche Indizes eignen sich hervorragend für Reporting-Szenarien, bei denen nur aktuelle oder aktive Datensätze benötigt werden.
Page Splits und Fragmentierung
Bei jedem INSERT prüft SQL Server, ob die Zielseite im Index noch Platz hat. Wenn nicht, wird die Seite geteilt – ein sogenannter Page Split. Dabei wird etwa die Hälfte der Zeilen auf eine neue Seite verschoben, und die Zeiger im B-Baum werden angepasst. Das ist ein teurer Vorgang: zusätzlicher Schreibaufwand, Log-Generierung und potenzielle Fragmentierung. Page Splits lassen sich durch einen passenden Fillfactor vermeiden, der zum Beispiel 10 Prozent freien Platz auf jeder Seite lässt. Beispiel:
CREATE INDEX IX_Orders_Date ON Sales.Orders(OrderDate) WITH (FILLFACTOR = 90);
Ein zu niedriger Fillfactor verschwendet Speicher, ein zu hoher provoziert Splits. Das ideale Maß hängt vom Schreibmuster ab. Analytische Systeme mit seltenen Inserts vertragen FILLFACTOR=100, stark genutzte OLTP-Tabellen eher 80 bis 90 Prozent.
Statistiken
Indizes funktionieren nur so gut wie ihre Statistiken. SQL Server erstellt automatisch Statistiken über Werteverteilungen, um optimale Ausführungspläne zu wählen. Sind diese veraltet, entstehen Fehlentscheidungen beim Join- oder Scan-Typ. Automatische Aktualisierung ist standardmäßig aktiv, aber bei großen Tabellen nicht immer zeitnah genug.
Mit folgendem Befehl können Statistiken gezielt erneuert werden:
UPDATE STATISTICS Sales.Orders WITH FULLSCAN;
Ein gepflegter Index mit aktuellen Statistiken ist oft wirksamer als ein schlecht gepflegter neuer Index.
Index Advisor
SQL Server bietet mit dem Database Tuning Advisor (DTA) und dem automatischen Index Advisor in Azure SQL Empfehlungen zur Indexerstellung. Diese Tools analysieren Workloads und schlagen neue Indizes vor. Doch Vorsicht: Sie berücksichtigen selten Gesamteffekte. Ein automatisch vorgeschlagener Index kann zwar eine einzelne Abfrage beschleunigen, gleichzeitig aber Inserts oder andere Queries verlangsamen.
Solche Empfehlungen sollten als Ausgangspunkt betrachtet werden – nicht als automatische Wahrheit. Ein erfahrener Datenbankadministrator prüft sie immer im Kontext von Systembelastung, Redundanz und Pflegekosten.
Welches Indizes fehlen?
Alternativ kann man sich vom SQL Server auch die Tabellen und Spalten auflisten, die möglicherweise fehlen und erstellt werden sollten. Aber Achtung! Diese Informationen werden aus Metadaten seit dem letzten Reboot des Dienstes gewonnen und sind mitunter nicht repräsentativ.
SELECT db_name(database_id) [Database], object_schema_name(object_id) [Schema], object_name(object_id) [Tabelle/ View], * FROM sys.dm_db_missing_index_details;
Der Kern dieser Abfrage ist die Database Management View (DMV) sys.dm_db_index_usage_stats.
Welche Indizes werden tatsächlich verwendet?
Recht einfach ist es auch herauszufinden, welche Indizes denn tatsächlich wie oft benutzt werden. Das ist sinnvoll, da oftmals auch gut durchdachte Indizes nicht (mehr) verwendet werden und es trotzdem zu keinen Performance-Problemen kommt. Damit stünden diese auf dem Prüfstand, ob sie überhaupt benötigt werden. Beispiel:
SELECT db_name(d.database_id) [Database], object_schema_name(d.object_id) [Schema], object_name(d.object_id) [Tabelle/ View], i.name [Index], d.* FROM sys.dm_db_index_usage_stats d LEFT OUTER JOIN sys.indexes AS i ON i.object_id = d.object_id AND i.index_id = d.index_id WHERE d.database_id = db_id();
Diesmal ist der Kern der DMV (Database Management View) sys.dm_db_index_usage_stats.
Indexpflege – ein regelmäßiges Muss
Ein guter Index braucht Pflege. Fragmentierung, veraltete Statistiken und ungenutzte Indizes wirken sich direkt auf die Performance aus. Mit System-Views wie sys.dm_db_index_physical_stats oder sys.dm_db_index_usage_stats lässt sich der Zustand leicht prüfen. Fragmentierung entsteht, wenn Daten auf unterschiedlichen Seiten landen – ähnlich wie zerschnittene Buchseiten. Ein Rebuild ordnet diese Seiten neu, während ein Reorganize sie nur sortiert. Die Schwelle liegt meist bei 10 bis 30 Prozent Fragmentierung.
Generell kann diese Pflege per Hand oder per (Dritthersteller)-Tool durchgeführt werden. Oder mit den bordeigenen SQL-Server-Wartungsplänen – nicht ganz perfekt, aber weit besser als gar keine Pflege.
Index-Design in der Realität
In der Praxis entstehen viele Indizes spontan – aus Query-Tuning, ORM-Generierung oder „nur mal eben schnell“. Das führt häufig zu Redundanzen. Zwei nahezu identische Nonclustered Indizes belasten Schreibvorgänge doppelt, ohne echten Mehrwert. Wird ein Index kaum genutzt, sollte er gelöscht oder konsolidiert werden. Das spart Speicher und Wartungskosten. Und wie zu überprüfen ist, welche Indizes tatsächlich verwendet werden, wurde ja schon oben gezeigt.
Indizes im Cloud-Zeitalter
In Azure SQL Database und Managed Instances gelten dieselben Grundprinzipien – jedoch mit zusätzlichen Aspekten: Cloud-Speicher ist skalierbar, aber langsamer. Deshalb wirken sich schlechte Indexstrategien hier noch stärker aus. Der automatische Tuning-Mechanismus in Azure kann Indizes selbst anlegen oder löschen, basierend auf Nutzungsstatistiken. Auch hier gilt: Vertrauen ist gut, Kontrolle ist besser. Monitoring und Telemetrie sollten regelmäßig geprüft werden.
Besonders interessant: Mit Intelligent Query Processing lernt Azure SQL, wann ein Index wirklich nötig ist – und kann Empfehlungen dynamisch anpassen. Das spart Zeit, ersetzt aber kein eigenes Verständnis.
Fazit
Ein Index ist wie ein Gewürz: Richtig eingesetzt, bringt er Geschmack und Leistung. Doch zu viele oder schlecht gewählte Indizes verderben das Menü. Wer seine Indexstruktur versteht, regelmäßig pflegt und bewusst dosiert, erzielt die perfekte Balance zwischen Performance und Aufwand.
SQL Server 2025 und Azure SQL bieten mit intelligentem Query Processing und automatischem Tuning mächtige Helfer – doch die Verantwortung für gutes Design bleibt beim Menschen. Ein bewusst gepflegtes Indexkonzept ist die Grundlage jeder performanten Datenbank.