SQL Server 2012 und 2016 Standard: Wenn ich die if-else-Logik in eine gespeicherte Prozedur setze, um abhängig vom Wert eines Parameters einen von zwei Codezweigen auszuführen, speichert das Modul die neueste Version im Cache?
Nein, es werden alle Versionen zwischengespeichert. Besser gesagt, es wird eine Version mit allen erkannten Pfaden zwischengespeichert , die mit übergebenen Variablen kompiliert wurden.
Hier ist eine kurze Demo mit der Stack Overflow-Datenbank.
Erstellen Sie einen Index:
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
Erstellen Sie eine gespeicherte Prozedur mit einem Indexhinweis, der auf einen nicht vorhandenen Index in verzweigtem Code verweist.
CREATE OR ALTER PROCEDURE dbo.YourMom (@Reputation INT)
AS
BEGIN
IF @Reputation = 1
BEGIN
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u WITH (INDEX = PK_Users_Id)
WHERE u.Reputation = @Reputation;
END;
IF @Reputation > 1
BEGIN
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u WITH (INDEX = ix_yourdad)
WHERE u.Reputation = @Reputation;
END;
END;
Wenn ich diesen gespeicherten Prozess auf der Suche nach Reputation = 1 ausführe, erhalte ich eine Fehlermeldung.
EXEC dbo.YourMom @Reputation = 1;
Meldung 308, Ebene 16, Status 1, Prozedur YourMom, Zeile 14 [Stapelstartzeile 32] Der Index 'ix_yourdad' für die Tabelle 'dbo.Users' (in der FROM-Klausel angegeben) ist nicht vorhanden.
Wenn wir den Indexnamen korrigieren und die Abfrage erneut ausführen, sieht der zwischengespeicherte Plan folgendermaßen aus:
In der XML-Datei befinden sich zwei Verweise auf die @Reputation
Variable.
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
Ein etwas einfacherer Test wäre, einfach einen geschätzten Plan für den gespeicherten Prozess zu erhalten. Sie können sehen, dass der Optimierer beide Pfade untersucht:
Und wenn sich bei der folgenden Ausführung der Wert des Parameters ändert, wird die gespeicherte Prozedur erneut kompiliert und zwischengespeichert, da ein anderer Zweig des Codes ausgeführt werden muss? (Diese Abfrage ist recht aufwendig zu kompilieren.) Vielen Dank.
Nein, der Laufzeitwert der ersten Kompilierung bleibt erhalten.
Wenn wir mit einem anderen Befehl erneut ausführen @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
Aus dem aktuellen Plan :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
Wir haben noch einen kompilierten Wert von 1, aber jetzt einen Laufzeitwert von 2.
Im Plan-Cache, den Sie mit einem kostenlosen Tool wie dem von meinem Unternehmen entwickelten testen können , sp_BlitzCache :
Die gespeicherte Prozedur wurde zweimal aufgerufen und jede Anweisung darin wurde einmal aufgerufen.
Also, was haben wir? Ein zwischengespeicherter Plan für beide Abfragen in der gespeicherten Prozedur.
Wenn Sie diese Art von Verzweigungslogik möchten , müssen Sie gespeicherte Prozeduren aufrufen:
CREATE OR ALTER PROCEDURE dbo.YourMom (@Reputation INT)
AS
BEGIN
IF @Reputation = 1
BEGIN
EXEC dbo.Reputation1Query;
END;
IF @Reputation > 1
BEGIN
EXEC dbo.ReputationGreaterThan1Query;
END;
END;
Oder dynamisches SQL:
DECLARE @sql NVARCHAR(MAX) = N''
SET @sql +=
N'
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u '
IF @Reputation = 1
BEGIN
SET @sql += N' (INDEX = PK_Users_Id)
WHERE u.Reputation = @Reputation;'
END;
IF @Reputation > 1
BEGIN
SET @sql += ' WITH (INDEX = ix_yourmom)
WHERE u.Reputation = @Reputation;'
END;
EXEC sys.sp_executesql @sql;
Hoffe das hilft!