(Frage von SO verschoben)
Ich habe eine Tabelle (Dummy-Daten) mit Clustered-Index enthält 2 Spalten:
Jetzt führe ich diese beiden Abfragen aus:
declare
@productid int =1 ,
@priceid int = 1
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid OR @productid IS NULL)
AND (priceid = @priceid OR @priceid IS NULL)
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid)
AND (priceid = @priceid)
Der tatsächliche Ausführungsplan für beide Abfragen lautet:
Wie Sie sehen können, verwendet der erste SCAN, während der zweite SEEK verwendet.
Durch Hinzufügen OPTION (RECOMPILE)
zur ersten Abfrage wurde der Ausführungsplan jedoch auch zur Verwendung von SEEK:
Freunde im DBA-Chat sagten mir:
In Ihrer Abfrage ist @ productid = 1, was bedeutet, dass (productID = @ productID ODER @productID IS NULL) zu (productID = @ productID) vereinfacht werden kann. Ersteres erfordert einen Scan, um mit einem beliebigen Wert von @productID zu arbeiten, letzteres könnte eine Suche verwenden. Wenn Sie also RECOMPILE verwenden, überprüft SQL Server, welchen Wert Sie tatsächlich in @productID haben, und erstellt den besten Plan dafür. Mit einem Wert ungleich Null in @productID ist eine Suche am besten. Wenn der Wert von @productID unbekannt ist, muss der Plan einem beliebigen Wert in @productID entsprechen, für den ein Scan erforderlich wäre. Seien Sie gewarnt: OPTION (RECOMPILE) erzwingt bei jeder Ausführung eine Neukompilierung des Plans, wodurch jeder Ausführung einige Millisekunden hinzugefügt werden. Dies ist jedoch nur dann ein Problem, wenn die Abfrage sehr häufig ausgeführt wird.
Ebenfalls :
Wenn @productID null ist, welchen Wert würden Sie suchen? Antwort: Es gibt nichts zu suchen. Alle Werte qualifizieren sich.
Ich verstehe, dass OPTION (RECOMPILE)
SQL Server gezwungen ist, zu sehen, welche tatsächlichen Werte die Parameter haben, und zu prüfen, ob es damit suchen kann.
Aber jetzt verliere ich den Vorteil der Vorauskompilierung.
Frage
IMHO - SCAN wird nur ausgeführt, wenn ein Parameter null ist.
Das ist in Ordnung - lassen Sie SQL SERVER einen Ausführungsplan für SCAN erstellen.
ABER wenn SQL Server sieht, dass ich diese Abfrage viele Male mit Werten 1,1
ausführe : Warum erstellt es dann keinen weiteren Ausführungsplan und verwendet dafür SEEK?
AFAIK - SQL erstellt einen Ausführungsplan für die am häufigsten getroffenen Abfragen .
Warum speichert SQL SERVER keinen Ausführungsplan für:
@productid int =1 , @priceid int = 1
(Ich führe es viele Male mit diesen Werten aus)
- Ist es möglich, SQL zu zwingen, diesen Ausführungsplan (der SEEK verwendet) für zukünftige Aufrufe beizubehalten?
quelle
Antworten:
Fassen wir einige der wichtigsten Punkte unserer Chatroom-Diskussion zusammen :
Im Allgemeinen speichert SQL Server für jede Anweisung einen einzelnen Plan zwischen . Dieser Plan muss für alle möglichen zukünftigen Parameterwerte gültig sein .
Es ist nicht möglich, einen Suchplan für Ihre Abfrage zwischenzuspeichern, da dieser Plan nicht gültig wäre, wenn beispielsweise @productid null ist.
In einigen zukünftigen Versionen unterstützt SQL Server möglicherweise einen einzelnen Plan, der abhängig von den Laufzeitparameterwerten dynamisch zwischen einem Scan und einer Suche wählt. Dies ist jedoch heute nicht der Fall.
Allgemeine Problemklasse
Ihre Abfrage ist ein Beispiel für ein Muster, das als "Catch All" - oder "Dynamic Search" -Abfrage bezeichnet wird. Es gibt verschiedene Lösungen mit jeweils eigenen Vor- und Nachteilen. In modernen Versionen von SQL Server (2008+) sind die Hauptoptionen:
IF
BlöckeOPTION (RECOMPILE)
sp_executesql
Die umfassendste Arbeit zu diesem Thema stammt wahrscheinlich von Erland Sommarskog, die in den Referenzen am Ende dieser Antwort enthalten ist. Es gibt keinen Ausweg aus der Komplexität, daher ist es notwendig, einige Zeit in das Ausprobieren jeder Option zu investieren, um die jeweiligen Kompromisse zu verstehen.
IF
BlöckeUm eine
IF
Blocklösung für den speziellen Fall in der Frage zu veranschaulichen :Diese enthält eine separate Anweisung für die vier möglichen Null- oder Nicht-Null-Fälle für jeden der beiden Parameter (oder lokalen Variablen), sodass es vier Pläne gibt.
Dort gibt es ein potenzielles Problem beim Parameter-Sniffing, für das möglicherweise ein
OPTIMIZE FOR
Hinweis zu jeder Abfrage erforderlich ist . Weitere Informationen zu diesen Feinheiten finden Sie im Abschnitt "Referenzen".Neu kompilieren
Wie oben und in der Frage erwähnt, können Sie auch einen
OPTION (RECOMPILE)
Hinweis hinzufügen , um bei jedem Aufruf einen neuen Plan (Suchen oder Scannen) zu erhalten. Angesichts der relativ langsamen Häufigkeit von Anrufen in Ihrem Fall (durchschnittlich alle zehn Sekunden mit einer Kompilierungszeit von weniger als einer Millisekunde) ist diese Option wahrscheinlich für Sie geeignet:Es ist auch möglich, Funktionen aus den oben genannten Optionen auf kreative Weise zu kombinieren, um die Vorteile jeder Methode optimal zu nutzen und gleichzeitig die Nachteile zu minimieren. Es gibt wirklich keine Abkürzung, um dieses Zeug im Detail zu verstehen und dann eine fundierte Entscheidung zu treffen, die durch realistische Tests unterstützt wird.
Weiterführende Literatur
RECOMPILE
Optionenquelle