Стандарт SQL Server 2012 та 2016: Якщо я вкладаю логіку if-else у збережену процедуру для виконання однієї з двох гілок коду, залежно від значення параметра, чи двигун кешує останню версію?
Ні, він кешує всі версії. А точніше, він кешує одну версію з усіма дослідженими шляхами , складеними з пропущеними змінними.
Ось коротка демонстрація за допомогою бази даних переповнення стека.
Створіть індекс:
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
Створіть збережену процедуру з підказкою індексу, яка вказує на індекс, який не існує, у розгалуженому коді.
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;
Якщо я виконую цю збережену програму в пошуку Reputation = 1, я отримую помилку.
EXEC dbo.YourMom @Reputation = 1;
Пост. 308, Рівень 16, Стан 1, Процедура YourMom, Рядок 14 [Початковий рядок пакету 32] Індекс 'ix_yourdad' у таблиці 'dbo.Users' (вказаний у пункті FROM) не існує.
Якщо ми виправимо ім'я індексу та повторно виконаємо запит, кешований план виглядає приблизно так:
Всередині XML буде два посилання на @Reputation
змінну.
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
Трохи простішим тестом було б просто отримати орієнтовний план для збереженого процесора. Ви можете побачити оптимізатор, що вивчає обидва контури:
І якщо при наступному виконанні значення параметра зміниться, чи буде він перекомпільований і повторно кешований збережена процедура, тому що повинна бути виконана інша гілка коду? (Цей запит складати досить дорого.) Дякую.
Ні, воно збереже значення часу виконання першої компіляції.
Якщо ми повторно виконаємо з іншим @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
З фактичного плану :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
У нас все ще є зібране значення 1, але зараз значення часу виконання 2.
У кеш-плані плану, який ви можете перевірити за допомогою безкоштовного інструменту, такого, як розробляє моя компанія, sp_BlitzCache :
Збережена процедура викликалася двічі, і кожне висловлення в ній було викликано один раз.
То що ми маємо? Один кешований план для обох запитів у збереженій процедурі.
Якщо вам потрібна така розгалужена логіка, вам доведеться зателефонувати до збережених процедур:
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;
Або динамічний 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;
Сподіваюся, це допомагає!