SQL Server 2012 i 2016 Standard: Jeśli wstawię logikę if-else do procedury składowanej w celu wykonania jednej z dwóch gałęzi kodu, w zależności od wartości parametru, czy silnik buforuje najnowszą wersję?
Nie, buforuje wszystkie wersje. A raczej buforuje jedną wersję ze wszystkimi zbadanymi ścieżkami , skompilowanymi z przekazanymi zmiennymi.
Oto szybkie demo z wykorzystaniem bazy danych przepełnienia stosu.
Utwórz indeks:
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
Utwórz procedurę składowaną z podpowiedzią indeksu wskazującą na indeks, który nie istnieje, w kodzie rozgałęzionym.
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;
Jeśli wykonam zapisany proc, szukając reputacji = 1, pojawia się błąd.
EXEC dbo.YourMom @Reputation = 1;
Msg 308, poziom 16, stan 1, procedura YourMom, wiersz 14 [wiersz partii wsadowej 32] Indeks „ix_yourdad” w tabeli „dbo.Users” (określony w klauzuli FROM) nie istnieje.
Jeśli naprawimy nazwę indeksu i ponownie uruchomimy kwerendę, buforowany plan wygląda następująco:
Wewnątrz XML będzie miał dwa odwołania do @Reputation
zmiennej.
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
Nieco prostszym testem byłoby po prostu oszacowanie planu dla przechowywanego proc. Optymalizator pokazuje obie ścieżki:
A jeśli w następnym wykonaniu wartość parametru ulegnie zmianie, czy ponownie skompiluje i ponownie buforuje procedurę przechowywaną, ponieważ należy wykonać inną gałąź kodu? (To zapytanie jest dość drogie do skompilowania.) Dziękujemy.
Nie, zachowa wartość czasu wykonywania pierwszej kompilacji.
Jeśli wykonamy ponownie z innym @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
Z aktualnego planu :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
Nadal mamy skompilowaną wartość 1, ale teraz wartość czasu wykonania 2.
W pamięci podręcznej planu, którą możesz sprawdzić za pomocą bezpłatnego narzędzia, takiego jak to, które rozwija moja firma, sp_BlitzCache :
Procedura składowana została wywołana dwukrotnie, a każda instrukcja w niej wywołana została raz.
Więc co mamy? Jeden buforowany plan dla obu zapytań w procedurze przechowywanej.
Jeśli chcesz tego rodzaju rozgałęzionej logiki, musisz wywołać procedury składowane w trybie podrzędnym:
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;
Lub dynamiczny 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;
Mam nadzieję że to pomoże!