SQL Server-Praxis

Abdeckende SQL Server-Indizes: Schlüssel und INCLUDE

Entwerfen Sie abdeckende SQL Server-Indizes anhand realer Filter und Sortierungen und bewerten Sie Lookup-Ersparnis gegen Schreib- und Speicherbedarf.

Ein Ausführungsplan kann einen Index Seek zeigen und trotzdem langsam sein. Nach dem Seek folgen möglicherweise Tausende Zugriffe auf den Clustered Index, um fehlende Ausgabespalten zu holen. Entscheidend ist deshalb der Aufwand des gesamten Zugriffs und nicht allein die Existenz einer Indexsuche.

Ein abdeckender Index enthält alle für eine bestimmte Abfrage benötigten Spalten. Abdeckung beschreibt die Beziehung zwischen Abfrage und Index. Eine zusätzliche SELECT-Spalte oder ein geändertes Prädikat kann diese Beziehung aufheben. Ausgangspunkt sollten die tatsächliche Anweisung und typische sowie außergewöhnliche Parameterwerte sein.

Den Schlüssel nach dem Zugriff entwerfen

Im Beispiel wird CustomerId auf einen Wert eingeschränkt. OrderedAt bildet eine Bereichsbedingung und die absteigende Sortierung; SaleId löst gleiche Zeitstempel eindeutig auf. Total und Status werden ausgegeben, bestimmen aber nicht die Navigation. Deshalb stehen diese beiden Spalten in INCLUDE.

CREATE TABLE #Sales
(
    SaleId bigint NOT NULL PRIMARY KEY,
    CustomerId int NOT NULL,
    OrderedAt datetime2(0) NOT NULL,
    Total decimal(12,2) NOT NULL,
    Status tinyint NOT NULL
);
INSERT #Sales VALUES
(1,7,'2021-10-01',90,1),(2,7,'2021-10-02',40,0),
(3,8,'2021-10-02',20,1),(4,7,'2021-10-03',70,1);

CREATE INDEX IX_Sales_Customer_Date
ON #Sales(CustomerId, OrderedAt DESC, SaleId DESC)
INCLUDE(Total, Status);

DECLARE @CustomerId int=7;
DECLARE @From datetime2(0)='2021-10-01';
SELECT TOP (2) SaleId, OrderedAt, Total, Status
FROM #Sales
WHERE CustomerId=@CustomerId AND OrderedAt>=@From
ORDER BY OrderedAt DESC, SaleId DESC;

SET STATISTICS IO, TIME ON;
SELECT SaleId, OrderedAt, Total, Status
FROM #Sales
WHERE CustomerId=@CustomerId AND OrderedAt>=@From;
SET STATISTICS IO, TIME OFF;
DROP TABLE #Sales;

Für Kunde 7 liefert die erste Abfrage die Verkäufe 4 und 2. Der Index unterstützt Kundenfilter und Reihenfolge. Stünde das Datum an erster Stelle, könnte SQL Server zwar den Zeitbereich lesen, müsste dabei aber möglicherweise viele andere Kunden prüfen. Die pauschale Regel, immer die insgesamt selektivste Spalte voranzustellen, reicht deshalb nicht aus.

Eine Bereichsbedingung begrenzt häufig, wie gut nachfolgende Schlüsselspalten die Suche weiter einschränken. Diese Spalten können dennoch Sortierung oder Abdeckung unterstützen. Prüfen Sie tatsächliche Seek-Prädikate und Restfilter. Das Verhältnis gelesener zu ausgegebenen Zeilen ist aussagekräftiger als die bloße Auflistung der Indexspalten.

INCLUDE gezielt einsetzen

Eingeschlossene Spalten liegen auf der Blattebene des Nonclustered Index und verändern dessen logische Suchordnung nicht. Sie können Lookups vermeiden, verbreitern aber die Blattseiten. Das benötigt Speicherplatz und Buffer-Pool-Kapazität, vergrößert Sicherungen und erzeugt Arbeit bei Änderungen. Eine häufig geänderte Statusspalte kann dadurch die Schreiblast messbar erhöhen.

Übernehmen Sie nicht sämtliche Spalten einer Empfehlung für einen fehlenden Index. Solche Hinweise betrachten nur einen begrenzten Ausschnitt geschätzter Abfragekosten und führen überlappende Anforderungen nicht automatisch zusammen. Vergleichen Sie vorhandene Indizes. Manchmal versorgt eine kleine Erweiterung mehrere wichtige Abfragen; unterschiedliche Schlüsselreihenfolgen können allerdings weiterhin fachlich nötig sein.

Ein Lookup ist nicht grundsätzlich schlecht. Zehn zusätzliche Zugriffe für zehn Ergebniszeilen können günstiger sein als ein sehr breiter, selten verwendeter Index. Der Umschlagpunkt hängt von Zeilenmenge, Cache, Zeilenbreite und alternativen Plänen ab. Hat ein Kunde zehn Verkäufe und ein anderer eine Million, gehören beide Fälle in die Bewertung.

Den gesamten Ablauf messen

Die temporäre Tabelle zeigt die erwarteten Ergebnisse, liefert aber keine belastbare Leistungsaussage. Verwenden Sie für die Messung einen repräsentativen Datenbestand und erfassen Sie logische Zugriffe, Laufzeit, CPU und tatsächliche Pläne. Kombinieren Sie enge und breite Kunden- und Zeitfilter. Leeren Sie nicht den produktiven Cache, um künstlich einen Kaltstart zu erzeugen.

Prüfen Sie außerdem Einfügevorgänge, Betragsänderungen, Statuswechsel und das Entfernen alter Daten. Entfällt eine Sortierung, vergleichen Sie auch Speicheranforderungen und mögliche Spills. Wählt eine umfangreiche Abfrage weiterhin einen Scan, kann dieser für die große Ergebnismenge richtig sein. Ein erzwungener Seek mit sehr vielen Lookups wäre möglicherweise teurer.

Eine Freigabe sollte benennen, welche Abfragen profitieren, welcher alte Index eventuell überflüssig wird und welche zusätzlichen Schreib- und Speicherkosten entstehen. Sichern Sie die vorherige Definition und beobachten Sie einen normalen Geschäftszyklus. Ein nützlicher abdeckender Index reduziert die Gesamtkosten der Arbeitslast und verbessert nicht nur die Darstellung im Ausführungsplan.

Technische Referenzen: Microsoft Learn: Included columns · Microsoft Learn: Index design.

Frage zu diesem Artikel

Haben Sie eine Frage zu diesem Thema?

Beschreiben Sie, was Sie bewerten oder wo Sie nicht weiterkommen. Wir antworten mit einer praktischen Empfehlung.

Inquiries are not enabled in this preview.

Eine Frage zu diesem Artikel stellen