Warum die INCLUDE-Klausel beim Erstellen eines Index verwenden?

431

Während des Studiums für die Prüfung 70-433 ist mir aufgefallen, dass Sie auf eine der beiden folgenden Arten einen Deckungsindex erstellen können.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

-- ODER --

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Die INCLUDE-Klausel ist für mich neu. Warum sollten Sie es verwenden und welche Richtlinien würden Sie vorschlagen, um zu bestimmen, ob ein Deckungsindex mit oder ohne die INCLUDE-Klausel erstellt werden soll?

Cory
quelle

Antworten:

363

Wenn sich die Spalte nicht in der WHERE/JOIN/GROUP BY/ORDER BY, sondern nur in der Spaltenliste in der SELECTKlausel befindet.

Die INCLUDEKlausel fügt die Daten auf der niedrigsten / Blattebene und nicht im Indexbaum hinzu. Dadurch wird der Index kleiner, da er nicht Teil des Baums ist

INCLUDE columnssind keine Schlüsselspalten im Index, daher sind sie nicht geordnet. Dies bedeutet, dass es für Prädikate, Sortieren usw., wie oben erwähnt, nicht wirklich nützlich ist. Es kann jedoch nützlich sein, wenn Sie in einigen Zeilen der verbleibenden Schlüsselspalte (n) eine Restsuche durchführen.

Ein weiterer MSDN-Artikel mit einem Beispiel

gbn
quelle
7
Dies wäre also eine Technik zum Erstellen einer kostengünstigeren Version eines abgedeckten Index?
JMarsch
3
@gbn, würde es Ihnen etwas ausmachen, diesen Satz ausführlicher zu erläutern und zu erklären, warum die include-Klausel für das Sortieren usw. nicht nützlich ist: "Die INCLUDE-Klausel fügt die Daten auf der niedrigsten / Blatt-Ebene und nicht im Indexbaum hinzu Dies macht den Index kleiner, weil er nicht Teil des Baumes ist "
Tola Odejayi
4
@JMarsch: Entschuldigung für die späte Antwort, aber ja, genau das ist es.
Gbn
10
@Tola Odejayi: INCLUDE-Spalten sind keine Schlüsselspalten im Index, daher sind sie nicht geordnet. Dies macht sie normalerweise nicht nützlich für JOINs oder Sortieren. Und weil sie keine Schlüsselspalten sind, sitzen sie nicht wie Schlüsselspalten in der gesamten B-Baum-Struktur
gbn
4
Obwohl dies die am meisten akzeptierte Antwort ist, denke ich, dass weitere Erklärungen erforderlich sind. Was ist, wenn die Spalte für einige Abfragen Teil der ist SELECTund für andere nicht? \
Chisko
215

Sie würden INCLUDE verwenden, um der Blattebene eines nicht gruppierten Index eine oder mehrere Spalten hinzuzufügen. Wenn Sie dies tun, können Sie Ihre Abfragen "abdecken".

Stellen Sie sich vor, Sie müssen die ID, die Abteilungs-ID und den Nachnamen eines Mitarbeiters abfragen.

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

Wenn Sie zufällig einen nicht gruppierten Index für (EmployeeID, DepartmentID) haben, müssen Sie jetzt, sobald Sie die Mitarbeiter für eine bestimmte Abteilung gefunden haben, eine "Lesezeichen-Suche" durchführen, um den tatsächlichen vollständigen Mitarbeiterdatensatz abzurufen, nur um die Nachname-Spalte zu erhalten . Das kann in Bezug auf die Leistung ziemlich teuer werden, wenn Sie viele Mitarbeiter finden.

Wenn Sie diesen Nachnamen in Ihren Index aufgenommen haben:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

Dann sind alle benötigten Informationen auf der Blattebene des nicht gruppierten Index verfügbar. Wenn Sie nur im nicht gruppierten Index suchen und Ihre Mitarbeiter für eine bestimmte Abteilung finden, verfügen Sie über alle erforderlichen Informationen, und die Lesezeichen-Suche für jeden im Index gefundenen Mitarbeiter ist nicht mehr erforderlich -> Sie sparen viel Zeit.

Natürlich können Sie nicht jede Spalte in jeden nicht gruppierten Index aufnehmen. Wenn Sie jedoch Abfragen haben, bei denen nur eine oder zwei Spalten fehlen, die "abgedeckt" werden sollen (und die häufig verwendet werden), kann es sehr hilfreich sein, diese einzuschließen in einen geeigneten nicht gruppierten Index.

marc_s
quelle
25
Sind Sie sicher, dass Sie diesen Index verwenden würden? Warum EmployeeID? Sie benötigen nur die Abteilungs-ID in den Schlüsselspalten? Sie wurden hier als maßgeblich zitiert: stackoverflow.com/q/6187904/27535
gbn
3
Ihre Erklärung ist gut, stimmt aber nicht mit dem von Ihnen skizzierten Anwendungsfall überein. Die Schlüsselspalte (n) sollten sich auf dem Filter oder den JOINSchlüsseln in der Abfrage befinden, und die INCLUDEs müssen die Daten sein, die Sie abrufen, aber nicht sortieren.
JNK
15
Zunächst wird der Index Employee (EmployeeID, DepartmentID) nicht zum Filtern von DepartmentID = 5 verwendet. Da die Reihenfolge nicht übereinstimmt
AnandPhadke
29

Diese Diskussion fehlt auf dem wichtigen Punkt aus: Die Frage ist nicht , ob die „nicht-Schlüssel-Spalten“ sind besser als umfassen Index -columns oder enthalten -columns.

Die Frage ist, wie teuer es ist, den Include-Mechanismus zu verwenden, um Spalten einzuschließen, die im Index nicht wirklich benötigt werden . (normalerweise nicht Teil der where-Klauseln, aber häufig in selects enthalten). Ihr Dilemma ist also immer:

  1. Verwenden Index ID1, ID2 ... idN allein oder
  2. Verwenden Sie den Index für id1, id2 ... idN und schließen Sie col1, col2 ... colN ein

Wobei: id1, id2 ... idN Spalten sind, die häufig in Einschränkungen verwendet werden, und col1, col2 ... colN Spalten sind, die häufig ausgewählt werden, aber normalerweise nicht in Einschränkungen verwendet werden

(Die Option, alle diese Spalten als Teil des Indexschlüssels einzuschließen, ist einfach immer albern (es sei denn, sie werden auch in Einschränkungen verwendet), da die Wartung immer teurer wäre, da der Index aktualisiert und sortiert werden muss, selbst wenn der "Schlüssel" haben sich nicht geändert).

Verwenden Sie also Option 1 oder 2?

Antwort: Wenn Ihre Tabelle selten aktualisiert wird - meistens eingefügt in / gelöscht von -, ist es relativ kostengünstig, den Include-Mechanismus zu verwenden, um einige "Hot Columns" einzuschließen (die häufig in Auswahlen verwendet werden - aber nicht oft für Einschränkungen verwendet werden) Beim Einfügen / Löschen muss der Index ohnehin aktualisiert / sortiert werden. Daher ist mit dem Speichern einiger zusätzlicher Spalten während der Aktualisierung des Index nur ein geringer zusätzlicher Aufwand verbunden. Der Overhead ist der zusätzliche Speicher und die CPU, die zum Speichern redundanter Informationen im Index verwendet werden.

Wenn die Spalten Sie betrachten hinzufügen , wie enthalten Säulen häufig aktualisiert werden (ohne den Index- Schlüssel -columns aktualisiert) - oder - wenn es so viele von ihnen ist , dass der Index der Nähe einer Kopie Ihrer Tabelle wird - Verwendung der Option 1 Ich würde vorschlagen! Auch wenn sich herausstellt, dass das Hinzufügen bestimmter Include-Spalten keinen Leistungsunterschied ergibt, können Sie die Idee des Hinzufügens überspringen :) Stellen Sie sicher, dass sie nützlich sind!

Die durchschnittliche Anzahl von Zeilen pro gleichen Werten in Schlüsseln (id1, id2 ... idN) kann ebenfalls von Bedeutung sein.

Beachten Sie, dass , wenn eine Spalte - das als ein zusätzlicher ist enthalten -Spalte der Index - in der verwendet wird Einschränkung : Solange der Index kann als solche verwendet werden (basierend auf Beschränkung gegen Index- Schlüssel -columns) - dann SQL Server Matching die Spaltenbeschränkung gegen den Index (Blattknotenwerte), anstatt den teuren Weg um die Tabelle selbst zu gehen.

Fredrik Solhaug
quelle
18

Grundlegende Indexspalten werden sortiert, eingeschlossene Spalten werden jedoch nicht sortiert. Dies spart Ressourcen bei der Verwaltung des Index und ermöglicht es dennoch, die Daten in den enthaltenen Spalten bereitzustellen, um eine Abfrage abzudecken. Wenn Sie also Abfragen behandeln möchten, können Sie die Suchkriterien zum Suchen von Zeilen in die sortierten Spalten des Index einfügen, dann aber zusätzliche, unsortierte Spalten mit Nicht-Suchdaten "einschließen". Es hilft definitiv dabei, das Sortieren und Fragmentieren bei der Indexpflege zu reduzieren.

onupdatecascade
quelle
7

Die Gründe dafür (einschließlich der Daten in der Blattebene des Index) wurden ausführlich erläutert. Der Grund, warum Sie diesbezüglich zwei Shakes geben, ist, dass SQL Server beim Ausführen Ihrer Abfrage, wenn die zusätzlichen Spalten nicht enthalten sind (neue Funktion in SQL 2005), zum Clustered-Index wechseln muss, um die zusätzlichen Spalten abzurufen Dies nimmt mehr Zeit in Anspruch und erhöht die Belastung des SQL Server-Dienstes, der Festplatten und des Speichers (genauer gesagt des Puffercaches), wenn neue Datenseiten in den Speicher geladen werden, wodurch möglicherweise andere häufig benötigte Daten aus dem Puffercache verschoben werden.

mrdenny
quelle
Gibt es eine Möglichkeit zu beweisen, dass tatsächlich weniger Speicher benötigt wird? Es ist das, was ich auch erwarten würde, aber ich bekomme etwas Statisches darüber bei der Arbeit
Asken
Da Sie die Seite vom Heap oder Clustered-Index in den Speicher sowie von der Indexseite laden müssen, was bedeutet, dass Sie doppelte Daten in den Speicher einfügen, wird die Mathematik ziemlich einfach. Eine Möglichkeit, dies spezifisch zu messen, gibt es nicht.
Mrdenny
5

Eine weitere Überlegung, die ich in den bereits gegebenen Antworten nicht gesehen habe, ist, dass eingeschlossene Spalten Datentypen haben können, die nicht als Indexschlüsselspalten zulässig sind, wie z. B. varchar (max).

Auf diese Weise können Sie solche Spalten in einen Deckungsindex aufnehmen. Ich musste dies kürzlich tun, um eine von nHibernate generierte Abfrage mit vielen Spalten in SELECT mit einem nützlichen Index bereitzustellen.

Robin Hames
quelle
3

Ein Grund, INCLUDESchlüsselspalten vorzuziehen , wenn Sie diese Spalte im Schlüssel nicht benötigen, ist die Dokumentation. Das macht die Entwicklung von Indizes in Zukunft viel einfacher.

Betrachten Sie Ihr Beispiel:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Dieser Index ist am besten geeignet, wenn Ihre Abfrage folgendermaßen aussieht:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

Natürlich sollten Sie keine Spalten einfügen, INCLUDEwenn Sie einen zusätzlichen Vorteil daraus ziehen können, dass sie im Schlüsselteil enthalten sind. Beide der folgenden Abfragen würden tatsächlich die col2Spalte im Schlüssel des Index bevorzugen .

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

Nehmen wir an, dass dies nicht der Fall ist und wir dies col2in der INCLUDEKlausel haben, da es einfach keinen Vorteil hat, es im Baumteil des Index zu haben.

Schneller Vorlauf einige Jahre.

Sie müssen diese Abfrage optimieren:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

Um diese Abfrage zu optimieren, wäre der folgende Index großartig:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

Wenn Sie überprüfen, welche Indizes Sie bereits für diese Tabelle haben, ist Ihr vorheriger Index möglicherweise noch vorhanden:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Jetzt wissen Sie das Col2und Col3sind nicht Teil des Indexbaums und werden daher weder zum Eingrenzen des gelesenen Indexbereichs noch zum Ordnen der Zeilen verwendet. Es ist ziemlich sicher, another_columnam Ende des Schlüsselteils des Index (nach col1) hinzuzufügen . Es besteht nur ein geringes Risiko, etwas zu beschädigen:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

Dieser Index wird größer, was immer noch einige Risiken birgt, aber es ist im Allgemeinen besser, bestehende Indizes zu erweitern, als neue einzuführen.

Wenn Sie einen Index ohne hätten INCLUDE, könnten Sie nicht wissen, welche Abfragen Sie durch Hinzufügen another_coldirekt danach brechen würden Col1.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

Was passiert, wenn Sie another_colzwischen Col1und hinzufügen Col2? Werden andere Fragen leiden?

Es gibt andere "Vorteile" INCLUDEgegenüber Schlüsselspalten, wenn Sie diese Spalten hinzufügen, um zu vermeiden, dass sie aus der Tabelle abgerufen werden . Ich halte den Dokumentationsaspekt jedoch für den wichtigsten.

Zur Beantwortung Ihrer Frage:

Welche Richtlinien würden Sie vorschlagen, um zu bestimmen, ob ein Deckungsindex mit oder ohne die INCLUDE-Klausel erstellt werden soll?

Wenn Sie dem Index eine Spalte hinzufügen, um diese Spalte im Index verfügbar zu machen, ohne die Tabelle zu besuchen, fügen Sie sie in die INCLUDEKlausel ein.

Wenn das Hinzufügen der Spalte zum Indexschlüssel zusätzliche Vorteile bringt (z. B. für order byoder weil der Leseindexbereich eingeschränkt werden kann), fügen Sie sie dem Schlüssel hinzu.

Eine längere Diskussion dazu können Sie hier lesen:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes

Markus Winand
quelle
2

Die Gesamtgröße aller in die Indexdefinition eingefügten Spalten ist begrenzt. Trotzdem musste ich noch nie einen so breiten Index erstellen. Für mich ist der größere Vorteil die Tatsache, dass Sie mehr Abfragen mit einem Index abdecken können, der Spalten enthält, da diese nicht in einer bestimmten Reihenfolge definiert werden müssen. Denken Sie an ist als Index innerhalb des Index. Ein Beispiel wäre die StoreID (wobei StoreID eine geringe Selektivität aufweist, was bedeutet, dass jedes Geschäft vielen Kunden zugeordnet ist) und dann demografische Kundendaten (Nachname, Vorname, Geburtsdatum): Wenn Sie diese Spalten nur in dieser Reihenfolge (StoreID, Nachname) einfügen , Vorname, DOB) können Sie nur effizient nach Kunden suchen, für die Sie StoreID und Nachname kennen.

Wenn Sie andererseits den Index für StoreID definieren und die Spalten LastName, FirstName und DOB einschließen, können Sie im Wesentlichen zwei Such-Index-Prädikate für StoreID ausführen und dann Prädikate für eine der enthaltenen Spalten suchen. Auf diese Weise können Sie alle möglichen Suchpermutationen abdecken, solange diese mit StoreID beginnen.

mEmENT0m0RI
quelle