Der Ausführungsplan verwendet NICHT INDEX, sondern den Tabellenscan

9

Ich weiß, wenn es um die Verwendung eines Index- oder Tabellenscans geht, verwendet SQL Server Statistiken, um festzustellen, welcher besser ist.

Ich habe eine Tabelle mit 20 Millionen Zeilen. Ich habe einen Index für (SnapshotKey, Measure) und diese Abfrage:

select Measure, SnapshotKey, MeasureBand
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

Die Abfrage gibt 500.000 Zeilen zurück. Die Abfrage wählt also nur 2,5% der Zeilen der Tabelle aus.

Die Frage ist, warum SQL Server nicht den nicht gruppierten Index verwendet, den ich habe, und stattdessen einen Tabellenscan verwendet.

Statistiken werden aktualisiert.

Gut zu erwähnen, dass die Abfrageleistung jedoch gut ist.

Tabellenscan

Tabellenscan

Erzwungener Index

Index erzwingen

Tabellen- / Indexstruktur

CREATE TABLE [t1](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [date] NOT NULL,
    [Measure] [nvarchar](30) NOT NULL,
    [MeasureBand] [nvarchar](30) NOT NULL,
    -- and many more fields
) ON [PRIMARY]

Keine PK auf dem Tisch, da es sich um ein Data Warehouse handelt.

CREATE NONCLUSTERED INDEX [nci_SnapshotKeyMeasure] ON [t1]
(
    [SnapshotKey] ASC,
    [Measure] ASC
)

quelle

Antworten:

16

Die Indexsuche ist möglicherweise nicht die beste Wahl, wenn Sie viele Zeilen zurückgeben und / oder die Zeilen sehr breit sind. Suchvorgänge können teuer sein, wenn Ihr Index nicht abdeckt. Siehe # 2 hier .

In Ihrem Szenario schätzt das Abfrageoptimierungsprogramm, dass die Durchführung von 50.000 einzelnen Suchvorgängen teurer ist als ein einzelner Scan. Die Wahl des Optimierers zwischen Scannen und Suchen (mit RID-Suchvorgängen für die Spalten, die von der Abfrage benötigt werden, aber nicht im nicht gruppierten Index vorhanden sind) basiert auf den geschätzten Kosten jeder Alternative.

Der Optimierer wählt immer die kostengünstigste Alternative, die er in Betracht zieht. Wenn Sie sich die Eigenschaft " Geschätzte Teilbaumkosten" im Stammknoten der beiden Ausführungspläne ansehen, werden Sie feststellen, dass der Scanplan niedrigere geschätzte Kosten aufweist als der Suchplan. Infolgedessen hat der Optimierer den Scan ausgewählt. Das ist im Wesentlichen die Antwort auf Ihre Frage.

Das vom Optimierer verwendete Kostenmodell basiert nun auf Annahmen und "magischen Zahlen", die mit den Leistungsmerkmalen Ihres Systems kaum übereinstimmen. Insbesondere wird im Modell davon ausgegangen, dass die Abfrage mit keiner der erforderlichen Daten oder Indexseiten ausgeführt wird, die sich bereits im Speicher befinden. Ein weiterer Grund ist, dass sequentielle E / A (für einen Scan erwartet) billiger sind als das zufällige E / A-Muster, das für RID-Lookups angenommen wird. Es gibt viele andere solche Annahmen und Vorbehalte, viel zu viele, um hier detailliert darauf einzugehen.

Es hat sich jedoch gezeigt, dass das Kostenmodell insgesamt allgemein "gut genug" Pläne für die meisten Abfragen, für die meisten Datenbankschemata, für die meisten Hardwarekonfigurationen, meistens überall, erstellt. Das ist eine ziemliche Leistung, wenn man darüber nachdenkt.

Modellbeschränkungen und andere Faktoren führen manchmal dazu, dass der Optimierer einen Plan auswählt, der tatsächlich überhaupt nicht "gut genug" ist. Sie berichten, dass "Leistung gut ist", so dass dies hier nicht der Fall zu sein scheint.

Aaron Bertrand
quelle
9

Sie haben tatsächlich 595.947 übereinstimmende Zeilen, was ungefähr 3% Ihrer Daten entspricht. Die Kosten für die Suche summieren sich also schnell. Angenommen, Sie haben 100 Zeilen pro Seite in Ihrer Tabelle, das sind 200.000 Seiten, die in einem Tabellenscan gelesen werden müssen. Das ist viel billiger als 595.947 Suchvorgänge.

Mit der GROUP BYKlausel in der Frage denke ich, dass Sie mit einem zusammengesetzten Schlüssel (Measure, SnapshotKey, MeasureBand) besser dran sind.

Schauen Sie sich den Vorschlag "Fehlender Index" an. Sie werden aufgefordert, Spalten einzuschließen, um die Suche zu vermeiden. Wenn Sie in Ihrer Abfrage auf andere Spalten verweisen, müssen diese im Allgemeinen in den Schlüsseln oder INCLUDEKlauseln des neuen Index enthalten sein. Andernfalls müssen noch 595.947 Suchvorgänge durchgeführt werden, um diese Werte zu erhalten.

Zum Beispiel für die Abfrage:

select Measure, SnapshotKey, MeasureBand, SUM(NumLoans), SUM(PrinBal)
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

...du bräuchtest:

CREATE INDEX ixWhatever 
ON t1 (Measure, SnapshotKey, MeasureBand) 
INCLUDE (NumLoans,PrinBal);
Rob Farley
quelle
6
  1. Das Feld in Ihrer WHERE-Bedingung ist nicht das führende Feld des Index.

  2. Sie haben measureNVARCHAR definiert. Stellen Sie dem Literal daher Folgendes voran N: where Measure = N'FinanceFICOScore'.

Erwägen Sie die Erstellung eines Clustered-Index für SnapshotKey. Wenn es eindeutig ist, kann es sich um eine PK (und Clustered) handeln. Wenn dies nicht eindeutig ist, kann es sich nicht um eine PK handeln, es kann sich jedoch dennoch um einen nicht eindeutigen Clustered-Index handeln. Dann würde sich Ihr nicht gruppierter Index nur in der measureSpalte befinden.

Und wenn man bedenkt, dass das erste Feld auch das GROUP BYist measure, würde das auch davon profitieren, measuredas führende Feld zu sein.

Tatsächlich müssen Sie für diesen Vorgang möglicherweise stattdessen den NonClustered-Index für Measure, SnapshotKey, MeasureBandgenau in der Reihenfolge definieren , in der er mit der GROUP BYKlausel übereinstimmt . In MeasureBandBezug auf die Größe wird dies nur wirklich hinzugefügt, da der NonClustered-Index bereits auf dem Index basiert Measureund MeasureKeybereits im Index enthalten ist, da er jetzt der Clustered-Index-Schlüssel ist (nein, Measurewird im NonClustered-Index nicht dupliziert).

@Rob hatte in einem jetzt gelöschten Kommentar zu seiner Antwort erwähnt, dass zur Lösung dieses Problems nur der NonClustered-Index mit diesen drei Feldern in dieser Reihenfolge definiert werden muss und dass das Erstellen eines Clustered-Index (nicht eindeutig) für SnapshotKeynicht erforderlich ist . Obwohl er wahrscheinlich Recht hat (ich hatte gehofft, dass weniger Felder funktionieren würden), würde ich dennoch behaupten, dass der Clustered Index nicht nur für diese Operation, sondern wahrscheinlich für die meisten anderen von Vorteil ist.

Solomon Rutzky
quelle
Die Diskussion zu dieser Antwort wurde in den Chat verschoben .
Paul White 9