it-swarm.com.de

Warum die INCLUDE-Klausel beim Erstellen eines Index verwenden?

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

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

- OR -

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

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

411
Cory

Befindet sich die Spalte nicht im WHERE/JOIN/GROUP BY/ORDER BY, aber nur in der Spaltenliste in der SELECT -Klausel.

Die INCLUDE -Klausel fügt die Daten auf der untersten/Blattebene hinzu und nicht in der Indexstruktur. Dadurch wird der Index kleiner, da er nicht Teil des Baums ist

INCLUDE columns sind keine Schlüsselspalten im Index, daher werden sie nicht sortiert. Das heißt, es ist nicht wirklich nützlich für Prädikate, Sortieren usw., wie ich oben erwähnt habe. Es kann jedoch nützlich sein , wenn Sie in einigen Zeilen der Schlüsselspalte (n) eine Residuensuche durchführen.

Ein weiterer MSDN-Artikel mit einem Beispiel

347
gbn

Sie würden INCLUDE verwenden, um der Blattebene eines nicht gruppierten Index eine oder mehrere Spalten hinzuzufügen. Auf diese Weise 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, sobald Sie die Mitarbeiter für eine bestimmte Abteilung gefunden haben, "Lesezeichen suchen", um den tatsächlichen vollständigen Mitarbeiterdatensatz abzurufen, nur um die Nachname-Spalte abzurufen . 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. Durch einfaches Durchsuchen des nicht gruppierten Index und Auffinden Ihrer Mitarbeiter für eine bestimmte Abteilung verfügen Sie über alle erforderlichen Informationen, und die Suche nach Lesezeichen für jeden Mitarbeiter im Index ist nicht mehr erforderlich -> Sie sparen viel Zeit.

Natürlich können Sie nicht jede Spalte in jeden nicht gruppierten Index einbeziehen. 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 einzufügen in einen geeigneten nicht gruppierten Index.

210
marc_s

In dieser Diskussion fehlt der wichtige Punkt: Die Frage ist nicht, ob die "Nicht-Schlüssel-Spalten" besser als Index - Spalten oder als eingeschlossen - Spalten einzuschließen sind .

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

  1. Verwenden Sie den Index für id1, id2 ... idN allein oder
  2. Verwenden Sie den Index für id1, id2 ... idN plus include col1, col2 ... colN

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, in der Regel jedoch nicht, das in Einschränkungen verwendet wird

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

Verwenden Sie also Option 1 oder 2?

Antwort: Wenn Ihre Tabelle nur selten aktualisiert wird - hauptsächlich in/aus eingefügt/gelöscht -, ist es relativ kostengünstig, den Include-Mechanismus zu verwenden, um einige "heiße Spalten" einzuschließen (die häufig in selects verwendet werden - aber nicht = wird häufig für Einschränkungen verwendet), da Einfügungen/Löschungen ohnehin eine Aktualisierung/Sortierung des Index erfordern und daher ein geringer zusätzlicher Aufwand mit dem Speichern einiger zusätzlicher Spalten verbunden ist, während der Index bereits aktualisiert wird. Der Overhead ist der zusätzliche Speicher und die CPU, die zum Speichern redundanter Informationen im Index verwendet werden.

Wenn die Spalten, die Sie als eingeschlossene Spalten hinzufügen möchten, häufig aktualisiert werden (ohne dass der Index -Schlüssel - aktualisiert wird) - oder - wenn es so viele davon sind der Index nähert sich einer Kopie Ihrer Tabelle - ich würde vorschlagen, Option 1 zu verwenden! Auch wenn sich herausstellt, dass das Hinzufügen bestimmter Include-Spalten keinen Leistungsunterschied verursacht - möchten Sie möglicherweise die Idee des Hinzufügens überspringen :) Vergewissern Sie sich, dass sie nützlich sind!

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

Beachten Sie, dass, wenn eine Spalte - die als eingeschlossen - Indexspalte hinzugefügt wird - in Einschränkung: verwendet wird, solange der Index als solcher vorhanden ist verwendet (basierend auf der Einschränkung gegen Index -Schlüssel - Spalten) - dann vergleicht SQL Server die Spaltenbeschränkung mit dem Index (Blattknotenwerte), anstatt den teuren Weg um das zu gehen Tisch selbst.

25
Fredrik Solhaug

Standardindexspalten werden sortiert, eingeschlossene Spalten werden jedoch nicht sortiert. Dies spart Ressourcen bei der Pflege des Index, während es weiterhin möglich ist, die Daten in den enthaltenen Spalten bereitzustellen, um eine Abfrage abzudecken. Wenn Sie also Abfragen abdecken möchten, können Sie die Suchkriterien festlegen, um Zeilen in den sortierten Spalten des Index zu suchen, aber dann zusätzliche, unsortierte Spalten mit Nicht-Suchdaten "einschließen". Dies hilft auf jeden Fall dabei, das Ausmaß der Sortierung und Fragmentierung bei der Indexpflege zu verringern.

17
onupdatecascade

Die Gründe dafür (einschließlich der Daten in der Blattebene des Index) wurden gut erklärt. Der Grund dafür ist, dass der SQL Server beim Ausführen Ihrer Abfrage, wenn keine zusätzlichen Spalten enthalten sind (neues Feature in SQL 2005), zum Clustered-Index wechseln muss, um die zusätzlichen Spalten abzurufen Dies nimmt mehr Zeit in Anspruch und erhöht die Auslastung des SQL Server-Dienstes, der Datenträger und des Speichers (genauer gesagt des Puffercaches), wenn neue Datenseiten in den Speicher geladen werden. Dadurch werden möglicherweise andere häufig benötigte Daten aus dem Puffercache entfernt.

6
mrdenny

Eine zusätzliche Überlegung, die ich in den bereits gegebenen Antworten nicht gesehen habe, ist, dass eingeschlossene Spalten Datentypen haben können, die als Indexschlüsselspalten nicht 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 und einem nützlichen Index bereitzustellen.

5
Robin Hames

Die Gesamtgröße aller Spalten in der Indexdefinition ist begrenzt. Trotzdem musste ich noch nie einen so breiten Index erstellen. Der größere Vorteil ist für mich, dass Sie mehrere 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 (bei der die StoreID eine geringe Selektivität aufweist, was bedeutet, dass jedes Geschäft vielen Kunden zugeordnet ist) und dann Kundendemografiedaten (LastName, FirstName, DOB): Wenn Sie diese Spalten in dieser Reihenfolge (StoreID, LastName) nur einreihen , Vorname, Geburtsdatum) können Sie nur effizient nach Kunden suchen, für die Sie StoreID und Nachname kennen.

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

2
mEmENT0m0RI

Ein Grund, warum Sie INCLUDE den Schlüsselspalten vorziehen sollten 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 in INCLUDE einfügen, wenn Sie einen zusätzlichen Vorteil daraus ziehen können, dass sie im Schlüsselteil enthalten sind. Die beiden folgenden Abfragen würden tatsächlich die Spalte col2 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, dies ist nicht der Fall und wir haben col2 In der INCLUDE -Klausel, weil es keinen Vorteil hat, es im Baumteil des Index zu haben .

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, über welche Indizes Sie bereits in dieser Tabelle verfügen, befindet sich Ihr vorheriger Index möglicherweise noch dort:

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

Jetzt wissen Sie, dass Col2 Und Col3 Nicht Teil des Indexbaums sind und daher weder zum Eingrenzen des gelesenen Indexbereichs noch zum Ordnen der Zeilen verwendet werden. Es ist ziemlich sicher, another_column An das Ende des Schlüsselteils des Indexes anzufügen (nach col1). Es besteht nur ein geringes Risiko, etwas zu zerbrechen:

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. Es ist jedoch im Allgemeinen besser, vorhandene Indizes zu erweitern, als neue einzuführen.

Wenn Sie einen Index ohne INCLUDE hätten, könnten Sie nicht wissen, welche Abfragen Sie durch Hinzufügen von another_col Direkt nach Col1 Unterbrechen würden.

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

Was passiert, wenn Sie another_col Zwischen Col1 Und Col2 Einfügen? Werden andere Fragen leiden?

Es gibt andere "Vorteile" von INCLUDE gegenüber Schlüsselspalten wenn Sie diese Spalten hinzufügen, nur 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 schlagen Sie vor, um zu bestimmen, ob ein Deckungsindex mit oder ohne INCLUDE-Klausel erstellt werden soll?

Wenn Sie dem Index eine Spalte hinzufügen, damit diese Spalte nur im Index verfügbar ist, ohne die Tabelle zu besuchen, fügen Sie sie in die INCLUDE -Klausel ein.

Wenn das Hinzufügen der Spalte zum Indexschlüssel zusätzliche Vorteile mit sich bringt (z. B. für order by Oder weil dadurch der gelesene Indexbereich 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

1
Markus Winand