Code erstellt einen anderen Plan, wenn er ad hoc als in einer gespeicherten Prozedur ausgeführt wird

9

Ich habe eine Löschanweisung, die einen schlechten Plan verwendet, wenn sie in einer gespeicherten Prozedur ausgeführt wird, aber einen viel besseren Plan wählt, wenn sie ad-hoc ausgeführt wird.

Ich habe alle Indizes für die von der Abfrage verwendeten Tabellen neu erstellt und alle Caches gelöscht. Der Optimierer wählt immer noch den falschen Plan für die gespeicherte Prozedur.

Ich möchte wissen, warum das Optimierungsprogramm für die gespeicherte Prozedur einen anderen Ausführungsplan verwendet als für Ad-hoc-SQL.

UPDATE: Ich denke, es müssen doch die Parameter gewesen sein. Wenn ich den Ad-hoc-Code mit der fest codierten Variablen ausgeführt habe, kann ich den "schlechten" Plan mit dem richtigen Wert erhalten (es ist ein Datum, Werte, die ein Jahr alt sind scheinen den "guten" Plan zu generieren). Nun zum Versuch, den "guten" Plan mithilfe eines Abfragehinweises auf den Prozess zu erzwingen.

LÖSUNG: Am Ende habe ich den gewünschten Plan mit dem Hinweis OPTIMIEREN FÜR UNBEKANNT erhalten.

msgisme
quelle
Es ist wahrscheinlich Parameterschnüffeln . Verweist die Abfrage auf eine Variable in ihrer WHEREKlausel?
Nick Chammas
4
Können Sie bitte Code hinzufügen
gbn
Veröffentlichen Sie die Pläne bitte auch irgendwo. So wie es ist, wird es ziemlich schwierig sein, Ihnen genau zu sagen, warum die Pläne anders sind.
Aaron Bertrand
OK, weitere Informationen: Ich kann die Pläne und Codes erst veröffentlichen, wenn ich sie ein wenig verschleiert habe. Ich werde versuchen, sie in ein paar zu bekommen. Der Plan für die gespeicherte Prozedur (fehlerhaft) führt einen Clustered-Index-Scan einer großen Tabelle durch (das Ganze, alle Partitionen). Anschließend werden mithilfe einer Schleife Zeilen aus einer kleineren Tabelle gefunden und anschließend aus der kleineren Tabelle gelöscht.
msgisme
Der Plan für den Ad-hoc-Code (gut) führt einen Tabellenscan der kleinen Tabelle (die nur 5 bis 10 Zeilen enthält) durch und verwendet einen nicht gruppierten Index der großen Tabelle, um zu ermitteln, welche Zeilen in der PK überprüft werden müssen des großen Tisches. Ich werde versuchen, die tatsächlichen Pläne so schnell wie möglich aufzustellen.
Msgisme

Antworten:

5

Übliche Verdächtige:

  1. Konstanten in Ad-hoc, Parameter im Code
  2. Nichtübereinstimmung der Datentypen im Code
  3. Parameter schnüffeln

Punkt 1: Der Optimierer kann den besten Plan für die Konstanten auswählen.
Ändern Sie die Konstanten = ändern Sie den Plan. Ein parametrisierter Plen ist resuierbar

Punkt 2 führt implizite Konvertierungen ein, da der Datentyp Vorrang hat,
z. B. die varchar-Spalte im Vergleich zum Parameter nvarchar

Punkt 3: Verwenden Sie die Parametermaskierung oder OPTIMIEREN SIE FÜR UNBEKANNT.
Bearbeiten: Zum Testen: Speichern Sie den gespeicherten Prozess, führen Sie sp_updatestats aus und führen Sie ihn erneut aus. Dadurch werden zwischengespeicherte Pläne ungültig, was besser ist als das Löschen des Plan-Cache

Bearbeiten: nach dem Kommentar von jcolebrand

Sie können das Schnüffeln auf verschiedene Arten deaktivieren. Die wichtigsten 3 sind

  • RECOMPILE. Das ist dumm IMO.
  • OPTIMIEREN (sic) FÜR UNBEKANNT
  • Parametermaskierung

Parametermaskierung:

DECLARE @MaskedParam varchar(10)
SELECT @MaskedParam = @SignaureParam

SELECT...WHERE column = @MaskedParam

Die Maskierung und der OPTIMIZE-Hinweis haben den gleichen Effekt (möglicherweise aus verschiedenen Gründen). Das heißt, der Optimierer muss Statistiken verwenden und die Datenverteilung ( Hinweis: wird noch von Mark Storey-Smith getestet ) die Parameter nach ihren eigenen Vorzügen bewerten . , anstatt was sie zuletzt angerufen hatten. Der Optimierer kann neu kompilieren oder nicht. SQL Server 2005 fügte eine Neukompilierung auf Anweisungsebene hinzu, sodass weniger Auswirkungen auftraten

Ich bin mir nicht sicher, warum ein Plan mit "beschnüffelten" Parametern im Vergleich zu maskierten / "unbekannten" Parametern "klebrig" ist.

Ich habe seit SQL Server 2000 die Parametermaskierung für alle außer dem einfachsten Code verwendet. Ich habe festgestellt, dass dies bei komplexerem Code wahrscheinlich vorkommt. Und bei meinem alten Job habe ich einige Berichtsprozesse, dass ich die Standardeinstellungen der Planparameter ändern könnte. Ich denke, der "Frachtkult" -Ansatz war einfacher als ein Support-Anruf.

Edit 2, 12. Oktober 2011, nach einigem Chat

  • Die Parametermaskierung und OPTIMIZE FOR UNKNOWN haben, soweit ich das beurteilen kann, den gleichen Effekt.
    Der Hinweis ist sauberer als die Maskierung, wurde jedoch mit SQL Server 2008 hinzugefügt.

  • Das Parameter-Sniffing erfolgt zur Kompilierungszeit.
    WITH RECOMPILE generiert bei jeder Ausführung einen neuen Plan. Dies bedeutet, dass eine schlechte Auswahl der Standardeinstellungen den Plan beeinflusst. Bei meinem letzten Job konnte ich dies leicht mit einem Berichtscode demonstrieren: Durch Ändern der Parameterstandards wurde der Plan unabhängig von den angegebenen Parametern geändert.

  • Dieser MS Connect-Artikel ist interessant: Suboptimale Indexverwendung innerhalb der gespeicherten Prozedur (in einer der folgenden SO-Antworten erwähnt)

  • Bob Beauchemin erwähnt es auch

Offene Fragen

  • Gilt das Schnüffeln immer noch mit WITH RECOMPILE? Das heißt, wenn der Optimierer weiß, dass er den Plan verwerfen muss, zielt er auf eine Wiederverwendung ab?

  • Warum sind schnüffelnde Pläne "klebrig"?

Links von SO:

gbn
quelle
1. Der Parameter in sp ist eine Variable im Code. 2. Wieder derselbe Datentyp. 3. Ich habe beide mit einer Vielzahl von Parametern ausgeführt und erhalte jedes Mal den gleichen Plan. Ich habe den Cache nach jedem Versuch geleert.
Msgisme
1
Betreff: Punkt 3. Sie können die Anweisung auch mit OPTION (RECOMPILE)oder mit dem gesamten Prozess ausführen WITH RECOMPILE, um SQL Server zu zwingen, vorhandene Pläne zu ignorieren.
Nick Chammas
3
Übrigens OPTIMIZE, weil Microsoft ein amerikanisches Unternehmen ist. :)
Nick Chammas
1
@Gbn irgendwelche Gedanken zum Verhindern / Besiegen von Parameter-Sniffing?
Jcolebrand
1
@jcolebrand: einfache Antwort ist "nein" :-)
gbn
2

Vergessen Sie nicht, dass die ANSI-Einstellungen, die Sie für den Verbindungsplan eingerichtet haben, eine Rolle bei der Auswahl des Ausführungsplans spielen. Wenn die App die gespeicherte Prozedur aufruft, hat sie wahrscheinlich andere ANSI-Einstellungen als Ihre SSMS-Verbindung.

mrdenny
quelle