Warum sollte SQL Server einen Index ignorieren?

16

Ich habe eine Tabelle CustPassMastermit 16 Spalten, von denen eine ist CustNum varchar(8), und ich habe einen Index erstellt IX_dbo_CustPassMaster_CustNum. Wenn ich meine SELECTAussage mache:

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

Der Index wird vollständig ignoriert. Das verwirrt mich, da ich eine andere Tabelle CustDataMastermit viel mehr Spalten habe (55), von denen eine ist CustNum varchar(8). Ich habe einen Index für diese Spalte ( IX_dbo_CustDataMaster_CustNum) in dieser Tabelle erstellt und verwende praktisch dieselbe Abfrage:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

Und es verwendet den Index, den ich erstellt habe.

Gibt es dafür eine bestimmte Begründung? Warum sollte es den Index von verwenden CustDataMaster, aber nicht den von CustPassMaster? Liegt es an der geringen Spaltenanzahl?

Die erste Abfrage gibt 66 Zeilen zurück. Für die zweite Zeile wird 1 Zeile zurückgegeben.

Zusätzlicher Hinweis: CustPassMaster4991 Datensätze und CustDataMaster5376 Datensätze. Könnte dies der Grund dafür sein, den Index zu ignorieren? CustPassMasterhat auch doppelte Datensätze, die die gleichen CustNumWerte haben. Ist das ein weiterer Faktor?

Ich stütze diese Behauptung auf die tatsächlichen Ausführungsplanergebnisse beider Abfragen.

Hier ist die DDL für CustPassMaster(die mit dem nicht verwendeten Index):

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Und die DDL für CustDataMaster(ich habe viele irrelevante Felder weggelassen):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Ich habe keinen Clustered-Index für eine dieser Tabellen, nur einen Nonclustered-Index.

Ignorieren Sie die Tatsache, dass die Datentypen nicht vollständig mit dem gespeicherten Datentyp übereinstimmen. Diese Felder sind eine Sicherung aus einer IBM AS / 400-DB2-Datenbank, und dies sind die kompatiblen Datentypen dafür. (Ich muss in der Lage sein, diese Sicherungsdatenbank mit denselben Abfragen abzufragen und dieselben Ergebnisse zu erzielen.)

Diese Daten werden nur für SELECTAussagen verwendet. Ich mache keine INSERT/ UPDATE/ DELETEAussagen auf sie, mit Ausnahme , wenn die Backup - Anwendung ist das Kopieren von Daten aus dem AS / 400.

Der Kommissar
quelle
Es könnte sich lohnen, diesen Artikel über den Wendepunkt von NonClustered zu Clustered zu lesen. sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
Mark Sinkinson
3
Das ist also der Unterschied. Wenn die erste Abfrage Ihren Index verwenden würde, müssten 65 Suchvorgänge ausgeführt werden. Das ist teuer. Die zweite Abfrage muss nur eine ausführen.
Aaron Bertrand

Antworten:

18

Normalerweise werden Indizes von SQL Server verwendet, wenn die Verwendung des Index für zweckmäßiger erachtet wird als die direkte Verwendung der zugrunde liegenden Tabelle.

Es scheint wahrscheinlich, dass der kostenbasierte Optimierer denkt, dass es teurer wäre, den fraglichen Index tatsächlich zu verwenden. Sie können sehen, dass der Index verwendet wird, wenn SELECT *Sie dies nicht einfach tun SELECT T1Col1.

Wenn Sie SELECT *SQL Server anweisen, alle Spalten in der Tabelle zurückzugeben. Zurückgeben dieser Spalten SQL Server muss die Seiten für die Zeilen, die den WHEREAnweisungskriterien entsprechen, aus der Tabelle selbst lesen (Clustered-Index oder Heap). SQL Server denkt wahrscheinlich, dass die Anzahl der Lesevorgänge, die erforderlich sind, um den Rest der Spalten aus der Tabelle abzurufen, auch dazu führen kann, dass die Tabelle direkt gescannt wird. Es wäre nützlich, die tatsächliche Abfrage und den von der Abfrage verwendeten tatsächlichen Ausführungsplan anzuzeigen.

Max Vernon
quelle
3
Eine naheliegendere und optimalere Lösung wäre es für mich, die von mir ausgewählten Spalten einzuschränken und sie in die INCLUDEKlausel des Index aufzunehmen.
Der Kommissar
1
Das könnte sehr wohl einen großen Unterschied machen. Wenn Sie der INCLUDEKlausel alle von der Abfrage zurückgegebenen Spalten hinzufügen, wird SQL Server wahrscheinlich den Index verwenden. Was versuchen Sie zu optimieren? Es scheint mir, wenn Ihre Tabelle eine durchschnittliche Zeilengröße von 100 Bytes hat, dann sind 5000 Zeilen nur etwa 500 KB Daten und es kann durchaus sein, dass es sich nicht lohnt, Zeit dafür zu investieren.
Max Vernon
1
Die durchschnittliche Zeilengröße beträgt 0,30 KB für Table1und 0,53 KB für Table2. Alle diese Daten werden von einem AS / 400-System (IBM System i) importiert und es befinden sich KEINE PKs auf irgendetwas. Ich habe heute alle Indizes manuell erstellt, nachdem die Leute erwähnt haben, dass die Anwendung zeitweise recht langsam ist.
Der Kommissar
10

Damit Sie den Index verwenden können, select *muss SQL Server zunächst alle Zeilen aus dem Index lesen, die mit dem Wert übereinstimmen, den Sie in der where-Klausel angegeben haben. Auf dieser Grundlage werden die Clustered-Index-Werte für jede Zeile abgerufen, und anschließend muss jede einzelne von ihnen separat vom Clustered-Index gesucht werden (= Schlüsselsuche). Da Sie angegeben haben, dass die Werte nicht eindeutig sind, verwendet SQL Server Statistiken, um abzuschätzen, wie oft diese Schlüsselsuche durchgeführt werden muss.

Höchstwahrscheinlich übersteigt der Kostenvoranschlag für das Scannen der nicht gruppierten Index- und Schlüsselsuche den Kostenvoranschlag für das Scannen des gruppierten Index, weshalb der Index ignoriert wird.

Sie können versuchen, set statistics io oneinen Indexhinweis zu verwenden, um festzustellen, ob die E / A-Kosten bei Verwendung des Index tatsächlich geringer sind oder nicht. Wenn der Unterschied groß ist, können Sie Statistiken einsehen, wenn diese nicht mehr aktuell sind.

Wenn in Ihrem SQL-Code tatsächlich Variablen und nicht die genauen Werte verwendet werden, kann dies auch durch Parameter-Sniffing verursacht werden (= der vorherige Wert, der zum Erstellen des Plans verwendet wurde, enthielt viele Zeilen in der Tabelle).

James Z
quelle
1

Das könnte der Grund sein. Die Optimierer sind kostenbasiert und entscheiden, welchen Pfad sie basierend auf den Kosten wählen, die jeder Ausführungspfad hat. Die 'größten' Kosten entstehen, wenn die Daten von der Festplatte in den Speicher übertragen werden. Wenn das Optimierungsprogramm berechnet, dass das Lesen des Index und der Daten länger dauert, wird möglicherweise der Index übersprungen. Je größer die Zeilen sind, desto mehr Plattenblöcke werden benötigt.

Marco
quelle