2017-08-22 1 views
1

Etwas stimmt hier nicht und ich verstehe nicht was. Es ist erwähnenswert, dort gesucht Wert ist nicht in der Tabelle, für einen bestehenden Wert gibt es kein Problem. Warum benötigt die erste Abfrage jedoch eine gruppierte Schlüsselsuche nach dem Primärschlüssel, die in der Abfrage nicht einmal verwendet wird, während die zweite direkt auf dem Index ausgeführt werden kann. Das Erzwingen der Abfrage zur Verwendung des Index WITH (INDEX (Indexname)) funktioniert zwar, aber warum wählt der Optimierer diese nicht selbst aus?SQL Server wählt den Index nicht, obwohl alles darauf hindeutet

Die Spalte PIECE_NUM befindet sich in keinem anderen Index und ist auch nicht der Primärschlüssel.

SET STATISTICS IO ON 

    DECLARE @vchEventNum VARCHAR(50) 
    SET @vchEventNum = '54235DDS28KC1F5SJQMWZ' 

    SELECT TOP 1 
      fwt.WEIGHT, 
      fwt.TEST_RESULT 
    FROM FIN_WEIGHT_TESTS fwt WITH(NOLOCK) 
    WHERE fwt.PIECE_NUM LIKE @vchEventNum + '%' 
    ORDER BY fwt.DTTM_INSERT DESC 


    SELECT TOP 1 
      fwt.WEIGHT, 
      fwt.TEST_RESULT 
    FROM FIN_WEIGHT_TESTS fwt WITH(NOLOCK) 
    WHERE fwt.PIECE_NUM LIKE '54235DDS28KC1F5SJQMWZ' + '%' 
    ORDER BY fwt.DTTM_INSERT DESC 

SET STATISTICS IO OFF 

Ich lasse beiden Abfragen in einer Batch-Lauf:

IO Statistiken berichten:

Abfrage 1: logische liest 16244910

Abfrage 2: logische liest 5

Table 'FIN_WEIGHT_TESTS'. Scan count 1, logical reads 16244910, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

Table 'FIN_WEIGHT_TESTS'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

Die Tabelle enthält einen nicht gruppierten Index für PIECE_NUM einschließlich aller drei anderen Spalten der Abfrage.

Hier sind die Abfrageausführungspläne (mit einer wenig Bearbeitung der tatsächlichen Namen entfernen): enter image description here

ich die convert_implicit bemerkt hat, aber das ist nur aufgrund der Umwandlung des varchar Parameters Spalt Nvarchar. Das Ändern des Parametertyps hat das Verhalten der Abfrage nicht geändert.

Warum verwendet die Abfrage mit dem Parameter nicht den Index, während der Parameter durch seinen Wert ersetzt wird?

+3

1 - Bitte ** nicht ** zufällig nolock verwenden: https://sqlstudies.com/2015/03/18/why-not-nolock/ – Milney

+0

2 - Lesen Sie auf Parameter Sniffing: https://www.brentozar.com/archive/2013/06/the-elephant-and-the-mouse-or-parameter-sniffing-in-sql-server/ – Milney

+0

@ Milney Danke für den Link, lesen Sie mehr über Nolock, wie es in unserer gesamten Datenbank stark verwendet wird. Hier ist es nicht mein Code, ich wollte nur herausfinden, warum diese Abfrage jeden Tag Milliarden von Lesevorgängen erzeugt. Wird auch über Parameter Sniffing lesen. Hier dachte ich, alle Hinweise gäbe es für den Optimierer zu wissen, was zu tun ist - nur eine bedingte Spalte, nur ein Index, in dem diese Spalte ist. – AndyZ

Antwort

2

Die erste Abfrage wird gescannt, weil Sie eine lokale Variable verwenden. Der Optimizer sieht dies als einen "anonymen" Wert und kann daher keine Statistiken verwenden, um einen guten Abfrageplan zu erstellen.

Die zweite Abfrage sucht, weil es ein Literalwert ist und SQL in seine Statistiken schauen kann und viel besser weiß, wie viele geschätzte Zeilen mit diesem Wert gefunden werden.

Wenn Sie führen Sie Ihre erste Abfrage wie folgt ich könnte mir vorstellen, Sie den besseren Plan verwenden sehen:

DECLARE @vchEventNum VARCHAR(50) 
SET @vchEventNum = '54235DDS28KC1F5SJQMWZ' 

SELECT TOP 1 
     fwt.WEIGHT, 
     fwt.TEST_RESULT 
FROM FIN_WEIGHT_TESTS fwt WITH(NOLOCK) 
WHERE fwt.PIECE_NUM LIKE @vchEventNum + '%' 
ORDER BY fwt.DTTM_INSERT DESC 
OPTION(RECOMPILE) 

Ich würde vorschlagen, eine parametrisierte Prozedur mit diesem Code auszuführen, um sicherzustellen, dass es eine im Cache gespeicherte Plan verwendet . Die Verwendung des RECOMPILE-Hinweises hat seine eigenen Nachteile, da der Optimierer den Plan jedes Mal neu erstellen muss, wenn er ausgeführt wird. Wenn Sie diesen Code sehr oft ausführen, würde ich diesen Hinweis vermeiden.

Sie über lokale Variablen lesen Sie hier: https://www.brentozar.com/archive/2014/06/tuning-stored-procedures-local-variables-problems/

+0

Das ist in Ordnung, aber immer noch keine Erklärung für WHY wählte Clustered + Non-Clustered wenn nicht geclustert hat alle angeforderten Felder – sepupic

+0

"vorschlagen, eine parametrisierte Prozedur verwenden" - nicht sicher, was Sie meinen. Der Code befindet sich in einer gespeicherten Prozedur, jedoch ist @vchEventNum eine lokale Variable innerhalb des SP. – AndyZ

+0

Wenn Sie eine lokale Variable verwenden, kann sie nicht gecropped werden, es sei denn, Sie verwenden die Option zum erneuten Kompilieren. Wenn Sie stattdessen @vchEventNum als Parameter von sp verwenden, wird es bei der ersten Proc-Kompilierung beschnüffelt und der Plan wird für diesen betrachteten Wert erstellt, die Schätzung wird genauer und der zweite Plan wird ausgewählt. – sepupic

1

Ich denke, die Ursache dessen, was passiert beide Verwendung von @variable und ORDER BY in Ihrer Anfrage ist. Um meine Schätzung zu testen, entfernen Sie order by aus Ihrer Abfrage und es kann in beiden Fällen zu gleichen Plänen führen (dieses Mal mit unterschiedlicher geschätzter Anzahl von Zeilen, die in select gemeldet wurden).Wie in der vorherigen Antwort erwähnt, können lokale Variablen zur Kompilierungszeit nicht beschnüffelt werden, da der Stapel als Ganzes betrachtet wird und nur die Option recompile dem Server erlaubt, den Wert einer Variablen zur Kompilierzeit zu kennen, wenn die Rekompilierung beginnt Die Variable ist bereits vergeben. Dies führt zu "Schätzung für unbekannt" im ersten Fall, d. H. Statistiken können nicht verwendet werden, da wir den Wert im Filter nicht kennen, es werden mehr Zeilen in der Ausgabe geschätzt.

Aber die Abfrage hat top + order by drin. Dies bedeutet, dass, wenn wir viele Zeilen erwarten, um nur eine, aber die erste von DTTM_INSERT DESC geordnet zu bekommen, müssen wir sort alle gefilterten Zeilen. In der Tat, wenn Sie den zweiten Plan betrachten, sehen Sie, dass der SORT Betreiber am meisten kostet. Wenn Sie jedoch die Konstante verwenden, verwendet SQL Server die Statistik und stellt fest, dass nur eine Zeile zurückgegeben wird, damit das Ergebnis sortiert werden kann.

Bei vielen erwarteten Zeilen wird der Index verwendet, der bereits von DTTM_INSERT bestellt wurde. Es ist nur meine Vermutung, weil Sie hier nicht die Erstellungsskripte für Ihre Indizes gepostet haben, aber aus dem Plan sehe ich, dass der erste Plan sicher in den gruppierten Index geht, um die Felder zu finden, die im nicht gruppierten Index fehlen dasselbe nicht geclustert, das im zweiten Fall verwendet wird, aber ich bin sicher, dass der im ersten Fall gewählte Index THE LEADING KEY COLUMN DTTM_INSERT hat. Doing so Server beseitigt die sort, die wir im zweiten Plan sehen

+0

Danke, muss Ihren Beitrag ein drittes Mal lesen, um alles zu bekommen, aber Sie hatten Recht, Entfernen der ORDER BY beseitigt den Scan und die Abfrageplan ist der gleiche (nur mit einer skalaren Berechnung und einem konstanten Scan für die Variable), aber die Reihenfolge nach ist notwendig, da es ältere Werte geben könnte. Ja, es gibt einen anderen Index nur für DTTM_INSERT. Trotzdem verstehe ich nicht, warum die erste Abfrage den Primärschlüssel durchsucht. – AndyZ

+0

Der erste Plan verwendet den Index mit DTTM_INSERT ** ORDERED **, um die SORT-Operation zu vermeiden.Aber es benötigt immer noch die fehlenden Felder, die es im Clustered-Index sucht. ORDERED ist hier der Schlüssel. Sie haben den Server gebeten, Ihnen einige Werte zurückzugeben, die MAX entsprechen (DTTM_INSERT) und der Index hat DTTM_INSERT-Werte bereits sortiert. Alles, was es jetzt braucht, ist, fehlende Werte zu erfassen und den Filter anzuwenden. – sepupic

+0

Danke, es wurde mir jetzt klarer. – AndyZ

Verwandte Themen