Harte und schnelle Regel für die Aufnahme von Spalten in den Index


38

Gibt es eine feste Regel, nach der entschieden werden muss, welche Spalten in welcher Reihenfolge in den nicht gruppierten Index aufgenommen werden sollen? Ich lese gerade diesen Beitrag https://stackoverflow.com/questions/1307990/why-use-the-include-clause-when-creating-an-index und ich fand , dass für die folgende Abfrage:

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

Das Poster schlug vor, einen Index wie diesen zu erstellen:

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

Hier kommt meine Frage, warum wir so keinen Index erstellen können

CREATE NONCLUSTERED INDEX NC_EmpDep 
      ON Employee( EmployeeID, DepartmentID, LastName)

oder

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

und was das Plakat dazu veranlasst, die LastName-Spalte beizubehalten. Warum nicht andere Spalten? und wie soll man entscheiden, in welcher Reihenfolge wir die Spalten dort behalten sollen?


3
INCLUDE sollte normalerweise die Felder enthalten, die Sie nach dem Auffinden eines Datensatzes benötigen. Auf diese Weise sparen Sie sich einen Roundtrip zurück, um mehr Daten zu erhalten. Die Reihenfolge der Felder im INCLUDE ist nicht wichtig.
Jimbo

Ryk, ich persönlich finde diesen Beitrag hilfreich.
Jason Young

Ich finde diese Frage auch hilfreich. Konzentrieren wir uns auf gute Fragen und gute Antworten, anstatt einzelne zu verfolgen ...
Volvox

Antworten:


47

Dieser Indexvorschlag von marc_s ist falsch. Ich habe einen Kommentar hinzugefügt. (Und es wurde auch meine Antwort angenommen!)

Der Index für diese Abfrage wäre

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

Ein Index ist in der Regel

CREATE INDEX <name> ON <table> (KeyColList) INCLUDE (NonKeyColList)

Woher:

  • KeyColList = Schlüsselspalten = werden für die Zeilenbeschränkung und -verarbeitung verwendet.
    WHERE, JOIN, ORDER BY, GROUP BY etc
  • NonKeyColList = Nichtschlüsselspalten = werden in SELECT und Aggregation (zB SUM (col)) nach Auswahl / Einschränkung verwendet

+1 - Ich bin damit einverstanden (siehe meine Ans), dass die Beispielindizes in OP für die Abfrage wertlos sind!
JNK

Groß! nur eine Sache mehr, was die Reihenfolge von KeyColList und NonKeyColList bestimmt. Kannst du das mit meinem Beispiel erklären? Angenommen, meine Abfrage lautet jetzt SELECT EmployeeID, DepartmentID, LastName FROM EmployeeWHERE DepartmentID = 5, StateID = 4. Wie sollte der Index jetzt sein?

@ Rocky - NonKeyColListBestellung spielt keine Rolle. KeyColListDie Reihenfolge sollte in der Reihenfolge der Häufigkeit liegen, in der sie voraussichtlich in Abfragen verwendet werden. Siehe meine Notizen zu meiner Antwort unten, aber es ist wie Last Name, First Name, Middile Initialin einem Telefonbuch. Sie benötigen das erste Feld, um das zweite Feld zu finden.
JNK

@gbn Benötigen wir wirklich EmployeeID in der Include-Liste? Wenn wir einen Clustered-Index für die EmployeeID-Spalte haben und darüber hinaus einen NonClustered-Index für die DeptId-Spalte erstellen, verweist der NonClustered-Index bereits auf den Clustering-Schlüssel, der in der NonClustered-Index-Struktur enthalten ist, einschließlich des Clustering-Schlüssels in der INCLUDE-Liste. Keine Vorteile hinzufügen.
Viswanathan Iyer

1
@ViswanathanIyer Es wird jedoch nicht zweimal zum tatsächlichen Festplattenspeicher hinzugefügt: SQL Server erkennt dies. Es wird also nicht benötigt, aber es macht die Dinge klarer. Wir kennen jedoch keine Clustered-Indizes in der Frage, daher ist es sicherer, keine anzunehmen.
7.

19

JNK und gbn haben großartige Antworten gegeben, aber es lohnt sich auch, das große Ganze zu betrachten - und sich nicht nur auf eine einzelne Abfrage zu konzentrieren. Obwohl diese spezielle Abfrage möglicherweise von einem Index (# 1) profitiert:

Employee(DepartmentID) INCLUDE (Lastname, EmployeeID)

Dieser Index hilft überhaupt nicht, wenn sich die Abfrage geringfügig ändert, z. B .:

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5 AND LastName = 'Smith'

Dies würde den Index (# 2) benötigen:

Employee(DepartmentID, LastName) INCLUDE (EmployeeID)

Stellen Sie sich vor, Sie hätten 1.000 Mitarbeiter in Abteilung 5. Wenn Sie den Index 1 verwenden, um alle Smiths zu finden, müssen Sie alle 1.000 Zeilen in Abteilung 5 durchsuchen, da die enthaltenen Spalten nicht Teil des Schlüssels sind. Mithilfe von Index 2 können Sie direkt zu Abteilung 5, Nachname Smith, navigieren.

Der Index Nr. 2 eignet sich daher besser für die Bearbeitung eines größeren Bereichs von Abfragen. Die Kosten sind jedoch ein aufgeblähterer Indexschlüssel, der die nichtblättrigen Seiten des Index vergrößert. Jedes System wird anders sein, daher gibt es hier keine Faustregel.


Als Randnotiz sei darauf hingewiesen, dass, wenn EmployeeID der Clustering-Schlüssel für diese Tabelle war - unter der Annahme eines Clustered-Index -, Sie EmployeeID nicht einschließen müssen - dies ist in allen nicht-Clustered-Indizes der Fall, was bedeutet, dass Index 2 genau dies könnte Sein

Employee(DepartmentID, LastName)

2
+1 für weitere nützliche Informationen. Für Ihren letzten Punkt habe ich dies getestet und die explizite Verwendung von EmployeeID in INCLUDE wird (basierend auf der Größe des Index) ignoriert, wenn EmployeeID der Clustered-Index ist. Es ist offensichtlicher, obwohl ich denke, und es gibt keinen Raum nach unten.
31.05.11

1
Ich stimme dir voll und ganz zu - es ist immer besser, explizit zu sein, besonders wenn es nichts kostet!

1
Nur für den Fall ... Ich meine, ich habe Clustered Key in INCLUDE getestet (nicht EmployeeID explizit) und es wird kein Leerzeichen hinzugefügt. In den Schlüsselspalten tut es.
1.

@gbn Ja, der Cluster-Schlüssel muss sich nur in der Blattebene des Index befinden, in der sich die INCLUDE-Spalten befinden. Das Verschieben in den Indexschlüssel würde bedeuten, dass es auch auf den nichtblättrigen Seiten vorhanden ist. Dies würde ein wenig aufblähen, aber keine schreckliche Menge (auf den Seiten der Zwischenebene würden Sie weitere 4 Bytes pro Blattebene hinzufügen, wenn Sie eine Ganzzahl annehmen).

Dies ist eine großartige Antwort, die einige der in diesem Artikel beschriebenen Effekte enthält: sqlperformance.com/2014/07/sql-indexes/… Wenn sich Ihre Abfrage ändert, ändern sich auch die Anforderungen Ihrer Indizes. Mit Jims Antwort könnten Sie besser dran sein, aber mit @gbn answer könnten Sie besser dastehen.
John aka hot2use

7

Ich bin mir nicht sicher, wie du zu dem ersten gekommen bist. Für diese Abfrage würde ich Folgendes verwenden:

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

In SQL gibt es für so ziemlich alles keine "harte und schnelle Regel".

In Ihrem Beispiel verwendet der Index jedoch nur das Feld, DepartmentIDweil es in der WHEREKlausel enthalten ist.

Die anderen Felder müssen von dort aus einfach erreichbar sein. Sie wählen basierend DepartmentIDdarauf, dass INCLUDEdiese Felder am Blattknoten des Index vorhanden sind.

Sie möchten keine anderen Beispiele verwenden, da diese für diesen Index nicht funktionieren.

Stellen Sie sich einen Index wie ein Telefonbuch vor. Die meisten Telefonbücher sind nach Nachname, Vorname und mittlerer Initiale sortiert. Wenn Sie den Vornamen einer Person kennen, aber nicht deren Nachnamen, ist das Telefonbuch nicht gut, da Sie nicht anhand der Reihenfolge des Telefonbuchindex nach dem Vornamen suchen können.

Die INCLUDEFelder sind wie Telefonnummer, Adresse usw. andere Informationen für jeden Eintrag im Buch.

BEARBEITEN:

Um weiter zu klären, warum man nicht benutzt:

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

Dieser Index ist nur nützlich, wenn Sie entweder EmployeeIDoder BEIDE EmployeeID und LastNamein Ihrer WHEREKlausel haben. Dies ist so ziemlich das GEGENÜBER von dem, was Sie für diese Abfrage benötigen.


@ajbeaven das ist wahr, weshalb der Kommentar, den ich in die Bearbeitung eingefügt habe, besagt, dass Sie entweder mitarbeiterID oder beide Spalten benötigen.
JNK

durr sorry misread :(
ajbeaven

0

Möglicherweise können Sie den Index (employee_id, department_id) weiterhin verwenden, aber Sie müssen eine 'Dummy'-Zeile in den where-Ausdruck einfügen, z. B .: "employee_id = employee_id)

  • einen Index für (employee_id, departemnent_id) haben,
  • nur nach einer department_id suchen / einschränken müssen
  • zu wissen, dass der Index nicht verwendet wird, da die Reihenfolge falsch ist (oder sich die Dinge inzwischen geändert haben und der folgende "Trick" nicht mehr benötigt wird. Ich bin ein "oldy"?) .
  • Benutze den "alten" Trick?

    Wählen Sie * aus Employee emp,
    wobei emp.employee_id = emp.employee_id
    und emp.department_id = 5

(Ich konzentriere mich also nicht auf den Include-Teil von Lastname, sondern darauf, ob der Schlüssel ja / oder nicht verwendet wird.)

Mit freundlichen Grüßen,

Miguell


2
Nein, das ist nutzlos und nicht effizient.
ypercubeᵀᴹ

Insbesondere muss weiterhin ein Indexscan durchgeführt werden, um jede Mitarbeiter-ID nach allen Instanzen von department_id 5 zu durchsuchen. Wenn 1000 Mitarbeiter und 5 Abteilungen vorhanden sind, muss SQL alle 1000 Mitarbeiter durchsuchen, um alle Zeilen für eine bestimmte Abteilung zu finden.
Mark Sowul

Betrachten Sie nun den umgekehrten Fall (Index ist auf department_id, employee_id). Natürlich ist es jetzt einfach, eine bestimmte Abteilung zu finden, aber beachten Sie auch, dass SQL nur 5 Abteilungen durchsuchen muss, um alle Zeilen für einen bestimmten Mitarbeiter zu finden, um einen bestimmten Mitarbeiter zu finden.
Mark Sowul
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.