Sehr gute Frage, da es sich um ein so wichtiges Konzept handelt. Dies ist jedoch ein großes Thema, und ich zeige Ihnen, dass es sich um eine Vereinfachung handelt, damit Sie die grundlegenden Konzepte verstehen können.
Erstens, wenn Sie Clustered Index Think Table sehen . Wenn eine Tabelle in SQL Server keinen Clustered-Index enthält, handelt es sich um einen Heap. Durch das Erstellen eines Clustered-Index für die Tabelle wird die Tabelle tatsächlich in eine Struktur vom Typ B-Tree umgewandelt. Ihr Clustered-Index IST Ihre Tabelle, er ist nicht von der Tabelle getrennt
Haben Sie sich jemals gefragt, warum Sie nur einen Clustered-Index haben können? Wenn wir zwei Clustered-Indizes hätten, bräuchten wir zwei Kopien der Tabelle. Es enthält schließlich die Daten.
Ich werde versuchen, dies anhand eines einfachen Beispiels zu erklären.
HINWEIS: Ich habe die Tabelle in diesem Beispiel erstellt und sie mit über 3 Millionen zufälligen Einträgen gefüllt. Dann liefen die eigentlichen Abfragen und fügten die Ausführungspläne hier ein.
Was Sie wirklich verstehen müssen, ist die O-Notation oder die betriebliche Effizienz . Angenommen, Sie haben die folgende Tabelle.
CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Hier haben wir also eine Basistabelle mit einem gruppierten Schlüssel auf CustomerID (der Primärschlüssel wird standardmäßig gruppiert). Somit wird die Tabelle basierend auf dem Primärschlüssel CustomerID angeordnet / geordnet. Die Zwischenebenen enthalten die CustomerID-Werte. Die Datenseiten enthalten die gesamte Zeile, also die Tabellenzeile.
Wir erstellen auch einen nicht gruppierten Index für das Feld Kundenname. Der folgende Code wird es tun.
CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer]
(
[CustomerName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
, DROP_EXISTING = OFF, ONLINE = OFF
, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
In diesem Index finden Sie also auf den Datenseiten / Knoten auf Blattebene einen Zeiger auf die Zwischenebenen im Clustered-Index. Der Index ist um das Feld Kundenname angeordnet / geordnet. Somit enthält die Zwischenebene die CustomerName-Werte und die Blattebene enthält den Zeiger (diese Zeigerwerte sind tatsächlich die Primärschlüsselwerte oder die CustomerID-Spalte).
Richtig also, wenn wir folgende Abfrage ausführen:
SELECT * FROM Customer WHERE CustomerID = 1
SQL liest den Clustered-Index wahrscheinlich über eine Suchoperation. Eine Suchoperation ist eine binäre Suche, die viel effizienter ist als eine Abtastung, die eine sequentielle Suche ist. In unserem obigen Beispiel wird der Index gelesen und durch die Verwendung einer binären Such-SQL können die Daten entfernt werden, die nicht den von uns gesuchten Kriterien entsprechen. Den Abfrageplan finden Sie im angehängten Screenshot.
Die Anzahl der Operationen oder die O-Notation für die Suchoperation ist also wie folgt:
- Führen Sie eine binäre Suche im Clustered-Index durch, indem Sie den gesuchten Wert mit den Werten auf der Zwischenebene vergleichen.
- Die übereinstimmenden Werte zurückgeben (denken Sie daran, dass der Clustered-Index alle Daten enthält und alle Spalten aus dem Index zurückgeben kann, da es sich um die Zeilendaten handelt.)
Es sind also zwei Operationen. Wenn wir jedoch die folgende Abfrage ausführen:
SELECT * FROM Customer WHERE CustomerName ='John'
SQL verwendet jetzt den nicht gruppierten Index für den Kundennamen, um die Suche durchzuführen. Da es sich jedoch um einen nicht gruppierten Index handelt, enthält dieser nicht alle Daten in der Zeile.
Daher durchsucht SQL die Zwischenebenen, um die passenden Datensätze zu finden, und durchsucht anschließend den geclusterten Index (auch als Tabelle bezeichnet) erneut nach den zurückgegebenen Werten, um die tatsächlichen Daten abzurufen. Das klingt verwirrend, ich weiß, aber lies weiter und alles wird klar.
Da unser nicht gruppierter Index nur das CustomerName-Feld (die in den Zwischenknoten gespeicherten indizierten Feldwerte) und den Zeiger auf die Daten enthält, bei denen es sich um die CustomerID handelt, enthält der Index keinen Datensatz des CustomerSurname. Der Kundenname muss aus dem gruppierten Index oder der gruppierten Tabelle abgerufen werden.
Beim Ausführen dieser Abfrage erhalte ich den folgenden Ausführungsplan:
In der obigen Abbildung sind zwei wichtige Punkte zu beachten
- SQL sagt, ich habe einen fehlenden Index (der Text ist grün). SQL schlägt vor, einen Index für CustomerName zu erstellen, der CustomerID und CustomerSurname enthält.
- Außerdem wird angezeigt, dass 99% der Zeit der Abfrage für die Schlüsselsuche im Primärschlüsselindex / Clustered-Index aufgewendet werden.
Warum schlägt SQL den Index für CustomerName erneut vor? Nun, da der Index nur die CustomerID und den CustomerName enthält, muss SQL noch den CustomerSurname aus der Tabelle / den Clustered-Indizes finden.
Wenn wir den Index erstellen und die Spalte CustomerSurname in den Index aufnehmen, kann SQL die gesamte Abfrage erfüllen, indem nur der nicht gruppierte Index gelesen wird. Aus diesem Grund schlägt SQL vor, meinen nicht gruppierten Index zu ändern.
Hier sehen Sie die zusätzliche Operation, die SQL ausführen muss, um die CustomerSurname-Spalte vom gruppierten Schlüssel abzurufen
Somit ist die Anzahl der Operationen wie folgt:
- Führen Sie eine binäre Suche für einen nicht gruppierten Index durch, indem Sie den gesuchten Wert mit den Werten auf der Zwischenebene vergleichen
- Lesen Sie für übereinstimmende Knoten den Knoten auf Blattebene, der den Zeiger für die Daten im Clustered-Index enthält (die Knoten auf Blattebene enthalten übrigens die Primärschlüsselwerte).
- Lesen Sie für jeden zurückgegebenen Wert den gruppierten Index (die Tabelle), um die Zeilenwerte hier herauszufinden. Wir würden dann den Kundennamen lesen.
- Gibt übereinstimmende Zeilen zurück
Das sind 4 Operationen, um die Werte herauszufinden. Doppelte Anzahl an Operationen im Vergleich zum Lesen des Clustered-Index. Das zeigt Ihnen, dass Ihr Clustered-Index Ihr leistungsstärkster Index ist, da er alle Daten enthält.
Also nur um einen letzten Punkt zu verdeutlichen. Warum sage ich, dass der Zeiger im nicht gruppierten Index der Primärschlüsselwert ist? Um zu demonstrieren, dass die Knoten auf Blattebene des nicht gruppierten Index den Primärschlüsselwert enthalten, ändere ich meine Abfrage in:
SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'
In dieser Abfrage kann SQL die CustomerID aus dem nicht gruppierten Index lesen. Der Clustered-Index muss nicht durchsucht werden. Dies können Sie dem Ausführungsplan entnehmen, der so aussieht.
Beachten Sie den Unterschied zwischen dieser Abfrage und der vorherigen Abfrage. Es gibt keine Suche. SQL kann alle Daten im nicht gruppierten Index finden
Hoffentlich können Sie anfangen zu verstehen, dass der Clustered-Index die Tabelle ist und Nicht-Clustered-Indizes NICHT alle Daten enthalten. Die Indizierung beschleunigt die Auswahl, da binäre Suchen durchgeführt werden können, aber nur Clustered-Indizes alle Daten enthalten. Daher führt eine Suche in einem nicht gruppierten Index fast immer dazu, dass Werte aus dem gruppierten Index geladen werden. Diese zusätzlichen Vorgänge führen dazu, dass nicht gruppierte Indizes weniger effizient sind als ein gruppierter Index.
Hoffe das klärt die Dinge auf. Wenn irgendetwas keinen Sinn ergibt, schreibe bitte einen Kommentar und ich versuche es zu klären. Es ist ziemlich spät hier und mein Gehirn fühlt sich ein bisschen platt. Zeit für einen roten Bullen.