Kurzfassung: Suchen ist viel besser
Weniger kurze Version: Die Suche ist im Allgemeinen viel besser, aber viele Suchvorgänge (z. B. aufgrund eines schlechten Abfrageentwurfs mit unangenehmen korrelierten Unterabfragen oder weil Sie viele Abfragen in einer Cursoroperation oder einer anderen Schleife ausführen) können schlechter sein als ein Scan, insbesondere wenn Ihre Abfrage möglicherweise Daten aus den meisten Zeilen in der betroffenen Tabelle zurückgibt.
Es ist hilfreich, die gesamte Familie für Datenfindungsvorgänge abzudecken, um die Auswirkungen auf die Leistung zu verstehen.
Tabellenscans: Da für Ihre Abfrage überhaupt keine Indizes relevant sind, muss der Planer einen Tabellenscan verwenden, bei dem jede Zeile überprüft wird. Dies kann dazu führen, dass jede Seite, die sich auf die Daten der Tabelle bezieht, von der Festplatte gelesen wird, was häufig der schlimmste Fall ist. Beachten Sie, dass bei einigen Abfragen auch dann eine Tabellensuche durchgeführt wird, wenn ein nützlicher Index vorhanden ist. Dies liegt normalerweise daran, dass die Daten in der Tabelle so klein sind, dass das Durchlaufen der Indizes mühsamer ist (wenn dies der Fall ist, würde man das erwarten) Planen Sie, Änderungen vorzunehmen, wenn die Daten wachsen, vorausgesetzt, das Maß für die Selektivität des Index ist gut.
Index-Scans mit Zeilensuchen: Wenn kein Index gefunden wird, der direkt für eine Suche verwendet werden kann, aber ein Index mit den richtigen Spalten vorhanden ist, kann ein Index-Scan verwendet werden. Wenn Sie beispielsweise eine große Tabelle mit 20 Spalten und einem Index für Spalte1, Spalte2, Spalte3 haben und das Problem auftritt SELECT col4 FROM exampletable WHERE col2=616
, ist es in diesem Fall col2
besser , den abzufragenden Index zu scannen, als die gesamte Tabelle zu scannen. Sobald übereinstimmende Zeilen gefunden wurden, müssen die Datenseiten gelesen werden, um col4 für die Ausgabe (oder das weitere Verknüpfen) abzurufen. Dies ist die Stufe der "Lesezeichensuche", wenn Sie sie in Abfrageplänen sehen.
Index-Scans ohne Zeilensuche: Wenn das obige Beispiel verwendet wurde, SELECT col1, col2, col3 FROM exampletable WHERE col2=616
ist der zusätzliche Aufwand zum Lesen von Datenseiten nicht erforderlich: Sobald übereinstimmende Indexzeilen col2=616
gefunden wurden, sind alle angeforderten Daten bekannt. Aus diesem Grund werden manchmal Spalten angezeigt, die nie durchsucht werden, aber wahrscheinlich zur Ausgabe angefordert werden und am Ende von Indizes hinzugefügt werden. Dadurch können Zeilensuchen gespart werden. Wenn Sie einem Index aus diesem Grund und nur aus diesem Grund Spalten hinzufügen, fügen Sie diese mit der INCLUDE
Klausel hinzu, um der Engine mitzuteilen, dass das Indexlayout für Abfragen auf der Grundlage dieser Spalten nicht optimiert werden muss (dies kann Aktualisierungen dieser Spalten beschleunigen). . Indexprüfungen können auch aus Abfragen ohne Filterklauseln resultieren: SELECT col2 FROM exampletable
Durchsucht diesen Beispielindex anstelle der Tabellenseiten.
Index-Suchvorgänge (mit oder ohne Zeilensuche) : Bei einem Suchvorgang wird nicht der gesamte Index berücksichtigt. Für die Abfrage kann SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567
die Abfrage-Engine die erste übereinstimmende Zeile finden, indem sie eine baumbasierte Suche im Index durchführt. c1
Anschließend kann sie den Index nacheinander durchsuchen, bis das Ende des Bereichs erreicht ist (dies gilt auch für eine Abfrage) für c1=1234
wie könnte es viele Zeilen sein , die Bedingung selbst für einen passenden =
Betrieb). Dies bedeutet, dass nur relevante Indexseiten (plus einige für die erste Suche erforderliche) anstelle jeder Seite im Index (oder in der Tabelle) gelesen werden müssen.
Clustered-Indizes: Bei einem Clustered-Index werden die Tabellendaten nicht in einer separaten Heap-Struktur, sondern in den Blattknoten dieses Index gespeichert. Dies bedeutet, dass nach dem Suchen von Zeilen mit diesem Index keine zusätzlichen Zeilensuchen erforderlich sind, unabhängig davon, welche Spalten benötigt werden [es sei denn, Sie haben Off-Page-Daten wie TEXT
Spalten oder VARCHAR(MAX)
Spalten mit langen Daten].
Aus diesem Grund können Sie nur einen Clustered-Index haben [1] . Der Clustered-Index ist Ihre Tabelle, anstatt eine separate Heap-Struktur zu haben. Wenn Sie also einen [2] verwenden, wählen Sie die Position sorgfältig aus, um den maximalen Gewinn zu erzielen.
Beachten Sie auch, dass der Clustered-Index als "Clustering-Schlüssel" für die Tabelle gilt und in jedem Nicht-Clustered-Index für die Tabelle enthalten ist, sodass ein Wide-Clustered-Index im Allgemeinen keine gute Idee ist.
[1] Sie können effektiv mehrere Clustered-Indizes definieren, indem Sie Nicht-Clustered-Indizes definieren, die jede Spalte in der Tabelle abdecken oder einschließen. Dies ist jedoch wahrscheinlich eine Verschwendung von Speicherplatz und hat Auswirkungen auf die Schreibleistung das musst du wirklich.
[2] Wenn ich sage „ wenn Sie einen Clustered - Index verwenden“, merken Sie, dass es in der Regel , dass Sie empfohlen tun haben auf jedem Tisch ein. Wie bei allen Faustregeln gibt es Ausnahmen. Tabellen, die nur Masseneinfügungen und ungeordnete Lesevorgänge sehen (Staging-Tabellen für ETL-Prozesse vielleicht), sind das häufigste Indikatorbeispiel.
Zusätzlicher Punkt: Unvollständige Scans:
Es ist wichtig zu bedenken, dass abhängig vom Rest der Abfrage ein Tabellen- / Index-Scan möglicherweise nicht die gesamte Tabelle scannt - wenn die Logik dies zulässt, kann der Abfrageplan möglicherweise dazu führen, dass die Abfrage vorzeitig abgebrochen wird. Das einfachste Beispiel hierfür ist SELECT TOP(1) * FROM HugeTable
: Wenn Sie sich den Abfrageplan dafür ansehen, werden Sie feststellen, dass nur eine Zeile vom Scan zurückgegeben wurde, und wenn Sie sich die E / A-Statistiken ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable
) ansehen, werden Sie feststellen, dass sie nur eine sehr kleine Zahl lesen von Seiten (vielleicht nur eine).
Dasselbe kann passieren, wenn das Prädikat einer WHERE
oder JOIN ... ON
-Klausel gleichzeitig mit dem Scan ausgeführt werden kann, bei dem es sich um die Quelle der Daten handelt. Der Abfrageplaner / -ausführer kann manchmal sehr geschickt sein, Prädikate in Richtung der Datenquellen zurückzuschieben, um auf diese Weise das vorzeitige Beenden von Überprüfungen zu ermöglichen (und manchmal können Sie Abfragen geschickt neu anordnen, um dies zu unterstützen!). Während die Daten gemäß den Pfeilen in der Standardanzeige des Abfrageplans von rechts nach links fließen, wird die Logik von links nach rechts ausgeführt, und jeder Schritt (von rechts nach links) wird nicht unbedingt vollständig ausgeführt, bevor der nächste beginnen kann. Wenn Sie im obigen einfachen Beispiel jeden Block im Abfrageplan als Agent betrachten SELECT
, fragt der TOP
Agent den Agenten nach einer Zeile, die wiederum den fragtTABLE SCAN
Agent für einen, dann SELECT
fragt der Agent nach einem anderen, aber der TOP
Agent weiß, dass es keinen Grund gibt, sich nicht einmal die Mühe zu machen, den Tabellenleser zu fragen SELECT
. Viele Operationen blockieren diese Art der Optimierung natürlich so oft in kompliziertere Beispiele eine Tabelle / Index - Scan wirklich nicht jede Zeile gelesen, aber darauf achten, nicht zu dem Schluss zu springen , dass jeder Scan eine teure Operation sein muss.