Saklı yordam ve plan önbelleğinde mantık


15

SQL Server 2012 ve 2016 Standardı:

if-elseBir parametrenin değerine bağlı olarak, kodun iki dalından birini yürütmek için depolanmış bir yordama mantık koyarsam , motor en son sürümü önbelleğe alıyor mu?

Ve aşağıdaki yürütmede, parametrenin değeri değişirse, kodun farklı bir dalı yürütülmesi gerektiğinden , saklı yordam yeniden derlenecek ve önbelleğe alınacak mı? (Bu sorgunun derlenmesi oldukça pahalıdır.)

Yanıtlar:


27

SQL Server 2012 ve 2016 Standardı: Bir parametrenin değerine bağlı olarak iki kod dalından birini yürütmek için if-else mantığını saklı bir yordama koyarsam, motor en son sürümü önbelleğe alıyor mu?

Hayır, tüm sürümleri önbelleğe alır . Daha doğrusu, değişkenleri iletilenlerle derlenen, keşfedilen tüm yolları içeren bir sürümü önbelleğe alır .

İşte Stack Overflow veritabanını kullanarak hızlı bir demo.

Bir dizin oluşturun:

CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO 

Dallanmayan kodda, var olmayan bir dizine işaret eden bir dizin ipucu ile saklı bir yordam oluşturun.

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;

Eğer depolanan proc Reputation = 1 arıyor yürütürsem, bir hata alıyorum.

EXEC dbo.YourMom @Reputation = 1;

Msg 308, Seviye 16, Durum 1, Yordam YourMom, Satır 14 [Toplu Başlangıç ​​Satırı 32] 'dbo.Users' tablosundaki 'ix_yourdad' dizini (FROM yan tümcesinde belirtilen) mevcut değil.

Dizin adını düzeltir ve sorguyu yeniden çalıştırırsak, önbelleğe alınan plan aşağıdaki gibi görünür:

Fındık

İçeride, XML @Reputationdeğişken için iki referansa sahip olacaktır .

<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />

Biraz daha basit bir test, depolanan proc için tahmini bir plan elde etmek olacaktır. Optimize edicinin her iki yolu da keşfettiğini görebilirsiniz:

Fındık

Ve aşağıdaki yürütmede, parametrenin değeri değişirse, kodun farklı bir dalı yürütülmesi gerektiği için saklı yordamı yeniden derleyip yeniden önbelleğe alacak mı? (Bu sorguyu derlemek oldukça pahalıdır.) Teşekkürler.

Hayır, ilk derlemenin çalışma zamanı değerini korur.

Farklı bir yöntemle yeniden çalıştırırsak @Reputation:

EXEC dbo.YourMom @Reputation = 2;

Gönderen gerçek planı :

<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />

Derlenmiş 1 değerine sahibiz, ancak şimdi 2 çalışma zamanı değerine sahibiz.

Şirketimin geliştirdiği gibi ücretsiz bir araçla kontrol edebileceğiniz plan önbelleğinde, sp_BlitzCache :

Fındık

Saklı yordam iki kez çağrıldı ve içindeki her ifade bir kez çağrıldı.

Peki neyimiz var? Saklı yordamda her iki sorgu için bir önbellek planı.

Eğer varsa istediğiniz dallı mantık bu tür, sen alt saklı yordamlar demelisiniz:

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;

Veya dinamik 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;

Bu yardımcı olur umarım!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.