Ich muss eine SELECT
Anweisung optimieren , aber SQL Server führt immer einen Index-Scan anstelle einer Suche durch. Dies ist die Abfrage, die sich natürlich in einer gespeicherten Prozedur befindet:
CREATE PROCEDURE dbo.something
@Status INT = NULL,
@IsUserGotAnActiveDirectoryUser BIT = NULL
AS
SELECT [IdNumber], [Code], [Status], [Sex],
[FirstName], [LastName], [Profession],
[BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE (@Status IS NULL OR [Status] = @Status)
AND
(
@IsUserGotAnActiveDirectoryUser IS NULL
OR
(
@IsUserGotAnActiveDirectoryUser IS NOT NULL AND
(
@IsUserGotAnActiveDirectoryUser = 1 AND ActiveDirectoryUser <> ''
)
OR
(
@IsUserGotAnActiveDirectoryUser = 0 AND ActiveDirectoryUser = ''
)
)
)
Und das ist der Index:
CREATE INDEX not_relevent ON dbo.Employee
(
[Status] DESC,
[ActiveDirectoryUser] ASC
)
INCLUDE (...all the other columns in the table...);
Der Plan:
Warum hat SQL Server einen Scan ausgewählt? Wie kann ich es reparieren?
Spaltendefinitionen:
[Status] int NOT NULL
[ActiveDirectoryUser] VARCHAR(50) NOT NULL
Statusparameter können sein:
NULL: all status,
1: Status= 1 (Active employees)
2: Status = 2 (Inactive employees)
IsUserGotAnActiveDirectoryUser kann sein:
NULL: All employees
0: ActiveDirectoryUser is empty for that employee
1: ActiveDirectoryUser got a valid value (not null and not empty)
sql-server
sql-server-2012
index
optimization
Bestter
quelle
quelle
@Status
?Status DESC
? Für wie viele Werte gibt esStatus
, wofür sind sie (wenn die Anzahl klein ist) und wird jeder Wert ungefähr gleich dargestellt? Zeigen Sie uns die Ausgabe vonSELECT TOP (20) [Status], c = COUNT(*) FROM dbo.Employee GROUP BY [Status] ORDER BY c DESC;
Antworten:
Ich glaube nicht, dass der Scan durch die Suche nach einer leeren Zeichenfolge verursacht wird (und obwohl Sie für diesen Fall einen gefilterten Index hinzufügen könnten, hilft dies nur bei sehr spezifischen Variationen der Abfrage). Es ist wahrscheinlicher, dass Sie Opfer von Parameter-Sniffing und einem einzelnen Plan sind, der nicht für alle verschiedenen Kombinationen von Parametern (und Parameterwerten) optimiert ist, die Sie für diese Abfrage bereitstellen.
Ich nenne dies das "Küchenspülen" -Verfahren , da Sie erwarten, dass eine Abfrage alle Dinge bereitstellt, einschließlich des Küchenspülbeckens.
Ich habe ein Video über meine Lösung für dieses Problem hier , aber im Grunde die beste Erfahrung , die ich für solche Fragen haben ist:
OPTION (RECOMPILE)
- Dies verhindert, dass bestimmte Parameterwerte den falschen Plan-Typ erzwingen. Dies ist besonders hilfreich, wenn Sie einen Datenversatz oder schlechte Statistiken haben oder wenn bei der ersten Ausführung einer Anweisung ein atypischer Wert verwendet wird, der zu einem anderen Plan führt als später und häufiger Hinrichtungen.optimize for ad hoc workloads
wird verhindert, dass nur einmal verwendete Abfragevariationen Ihren Plan-Cache verschmutzen.Aktivieren Sie die Optimierung für Ad-hoc-Workloads:
Ändern Sie Ihre Vorgehensweise:
Sobald Sie eine Arbeitslast haben, die auf diesen Abfragen basiert, die Sie überwachen können, können Sie die Ausführungen analysieren und feststellen, welche von zusätzlichen oder unterschiedlichen Indizes am meisten profitieren würden - Sie können dies aus verschiedenen Blickwinkeln tun, aus einfachen "welcher Kombination von Parameter werden am häufigsten angegeben? " zu "Welche einzelnen Abfragen haben die längsten Laufzeiten?" Wir können diese Fragen nicht nur anhand Ihres Codes beantworten. Wir können nur vorschlagen, dass ein Index nur für eine Teilmenge aller möglichen Parameterkombinationen hilfreich ist, die Sie unterstützen möchten. Zum Beispiel wenn
@Status
Ist NULL, ist keine Suche nach diesem nicht gruppierten Index möglich. In den Fällen, in denen sich Benutzer nicht um den Status kümmern, erhalten Sie einen Scan, es sei denn, Sie haben einen Index, der den anderen Klauseln entspricht (aber ein solcher Index ist angesichts Ihrer aktuellen Abfragelogik auch nicht hilfreich - entweder leere Zeichenfolge oder nicht leere Zeichenfolge ist nicht genau selektiv).In diesem Fall ist dies abhängig von der Menge der möglichen
Status
Werte und der Verteilung dieser WerteOPTION (RECOMPILE)
möglicherweise nicht erforderlich. Wenn Sie jedoch einige Werte haben, die 100 Zeilen ergeben, und einige Werte, die Hunderttausende ergeben, möchten Sie sie möglicherweise dort haben (sogar auf CPU-Kosten, die angesichts der Komplexität dieser Abfrage marginal sein sollten), damit Sie dies können in so vielen Fällen wie möglich suchen. Wenn der Wertebereich endlich genug ist, können Sie mit dem dynamischen SQL sogar etwas Kniffliges tun, indem Sie sagen: "Ich habe diesen sehr selektiven Wert für@Status
. Wenn dieser bestimmte Wert übergeben wird, nehmen Sie diese geringfügige Änderung am Abfragetext vor Dies wird als eine andere Abfrage betrachtet und für diesen Parameterwert optimiert. "quelle
Haftungsausschluss : Einige der Dinge in dieser Antwort können einen DBA zusammenzucken lassen. Ich gehe es von einem reinen Performance-Standpunkt aus an - wie man Index-Suchanfragen erhält, wenn man immer Index-Scans erhält.
Damit geht es aus dem Weg.
Ihre Abfrage wird als "Abfrage für Küchenspülen" bezeichnet - eine einzelne Abfrage, die eine Reihe möglicher Suchbedingungen abdeckt. Wenn der Benutzer
@status
einen Wert festlegt , möchten Sie nach diesem Status filtern. Wenn dies der Fall@status
istNULL
, geben Sie alle Status usw. zurück.Dies führt zu Problemen bei der Indizierung, die jedoch nicht mit der Sargabilität zusammenhängen, da alle Suchbedingungen den Kriterien entsprechen.
Das ist sargable:
Dies ist nicht sargable, da SQL Server
ISNULL([status], 0)
für jede Zeile auswerten muss, anstatt einen einzelnen Wert im Index nachzuschlagen :Ich habe das Problem mit dem Spülbecken in einer einfacheren Form nachgebildet:
Wenn Sie Folgendes versuchen, erhalten Sie einen Index-Scan, obwohl A die erste Spalte des Index ist:
Dies führt jedoch zu einer Indexsuche:
Solange Sie eine überschaubare Anzahl von Parametern verwenden (in Ihrem Fall zwei), könnten Sie wahrscheinlich nur
UNION
eine Reihe von Suchabfragen durchführen - im Grunde alle Permutationen von Suchkriterien. Wenn Sie drei Kriterien haben, sieht dies chaotisch aus, bei vier ist es völlig unüberschaubar. Du wurdest gewarnt.Damit der dritte dieser vier einen Index-Suchvorgang verwenden kann, benötigen Sie jedoch einen zweiten Index
(B, A)
. So könnte Ihre Abfrage mit diesen Änderungen aussehen (einschließlich meines Refactorings der Abfrage, um sie besser lesbar zu machen).... und Sie benötigen einen zusätzlichen Index,
Employee
wobei die beiden Indexspalten vertauscht sind.Der Vollständigkeit halber sollte ich erwähnen, dass dies
x=@x
implizit bedeutet, dassx
es nicht sein kann,NULL
weilNULL
es niemals gleich istNULL
. Das vereinfacht die Abfrage etwas.Und ja, Aaron Bertrands dynamische SQL-Antwort ist in den meisten Fällen die bessere Wahl (dh wann immer Sie mit den Neukompilierungen leben können).
quelle
Ihre grundlegende Frage scheint "Warum" zu sein, und ich denke, Sie finden die Antwort auf ungefähr 55 Minuten dieser großartigen Präsentation von Adam Machanic auf der TechEd vor einigen Jahren.
Ich erwähne die 5 Minuten in Minute 55, aber die gesamte Präsentation ist die Zeit wert. Wenn Sie sich den Abfrageplan für Ihre Abfrage ansehen, werden Sie sicher feststellen, dass er Restprädikate für die Suche enthält. Grundsätzlich kann SQL nicht alle Teile des Index "sehen", da einige von ihnen durch Ungleichungen und andere Bedingungen verborgen sind. Das Ergebnis ist ein Index-Scan für eine Supermenge basierend auf dem Prädikat. Dieses Ergebnis wird gespoolt und dann unter Verwendung des verbleibenden Prädikats erneut gescannt.
Überprüfen Sie die Eigenschaften des Scan-Operators (F4) und prüfen Sie, ob die Eigenschaftsliste sowohl "Prädikat suchen" als auch "Prädikat" enthält.
Wie andere angegeben haben, ist die Abfrage so wie sie ist schwer zu indizieren. Ich habe in letzter Zeit an vielen ähnlichen gearbeitet und jede hat eine andere Lösung benötigt. :(
quelle
Bevor wir uns fragen, ob die Indexsuche dem Indexscan vorgezogen wird, besteht eine Faustregel darin, zu überprüfen, wie viele Zeilen im Vergleich zu den Gesamtzeilen der zugrunde liegenden Tabelle zurückgegeben werden. Wenn Sie beispielsweise erwarten, dass Ihre Abfrage 10 von 1 Million Zeilen zurückgibt, wird die Indexsuche wahrscheinlich dem Index-Scan vorgezogen. Wenn jedoch einige tausend Zeilen (oder mehr) von der Abfrage zurückgegeben werden sollen, wird die Indexsuche möglicherweise NICHT unbedingt bevorzugt.
Ihre Anfrage ist nicht komplex. Wenn Sie also einen Ausführungsplan veröffentlichen können, haben wir möglicherweise bessere Ideen, um Sie zu unterstützen.
quelle
Dies ist nur das Originalformat
Dies ist die Revision - nicht 100% sicher, aber (vielleicht) probieren Sie es aus,
sogar ein ODER wird wahrscheinlich ein Problem sein,
das bei ActiveDirectoryUser null brechen würde
quelle