SQL Server - Jika logika dalam prosedur tersimpan dan cache rencana

15

SQL Server 2012 dan 2016 Standar:

Jika saya meletakkan if-elselogika dalam prosedur tersimpan untuk mengeksekusi salah satu dari dua cabang kode, tergantung pada nilai parameter, apakah mesin cache versi terbaru?

Dan jika pada eksekusi berikut, nilai parameter berubah, apakah ia akan mengkompilasi ulang dan me-cache kembali prosedur yang tersimpan , karena cabang kode yang berbeda harus dieksekusi? (Permintaan ini cukup mahal untuk dikompilasi.)

Nicole G.
sumber

Jawaban:

27

SQL Server 2012 dan 2016 Standar: Jika saya meletakkan logika if-else dalam prosedur tersimpan untuk mengeksekusi salah satu dari dua cabang kode, tergantung pada nilai parameter, apakah mesin cache versi terbaru?

Tidak, cache semua versi. Atau lebih tepatnya, cache satu versi dengan semua jalur dieksplorasi, dikompilasi dengan variabel yang diteruskan.

Ini demo singkat, menggunakan database Stack Overflow.

Buat indeks:

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

Buat prosedur tersimpan dengan petunjuk indeks yang menunjuk ke indeks yang tidak ada, dalam kode bercabang.

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;

Jika saya menjalankan proc yang disimpan itu mencari Reputasi = 1, saya mendapatkan kesalahan.

EXEC dbo.YourMom @Reputation = 1;

Msg 308, Level 16, Status 1, Prosedur Youromom, Line 14 [Batch Start Line 32] Indeks 'ix_yourdad' pada tabel 'dbo.Users' (ditentukan dalam klausa FROM) tidak ada.

Jika kami memperbaiki nama indeks dan menjalankan kembali kueri, paket yang di- cache terlihat seperti ini:

Gila

Di dalam, XML akan memiliki dua referensi ke @Reputationvariabel.

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

Tes yang sedikit lebih sederhana adalah dengan hanya mendapatkan perkiraan rencana untuk proc yang disimpan. Anda dapat melihat pengoptimal mengeksplorasi kedua jalur:

Gila

Dan jika pada eksekusi berikut, nilai parameter berubah, apakah ia akan mengkompilasi ulang dan me-cache kembali prosedur yang tersimpan, karena cabang kode yang berbeda harus dieksekusi? (Permintaan ini cukup mahal untuk dikompilasi.) Terima kasih.

Tidak, itu akan mempertahankan nilai runtime dari kompilasi pertama.

Jika kami menjalankan kembali dengan yang berbeda @Reputation:

EXEC dbo.YourMom @Reputation = 2;

Dari rencana aktual :

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

Kami masih memiliki nilai kompilasi 1, tapi sekarang nilai runtime 2.

Dalam cache rencana, yang dapat Anda periksa dengan alat gratis seperti yang dikembangkan perusahaan saya, sp_BlitzCache :

Gila

Prosedur tersimpan telah dipanggil dua kali, dan setiap pernyataan di dalamnya telah dipanggil sekali.

Jadi apa yang kita miliki? Satu paket cache untuk kedua pertanyaan dalam prosedur tersimpan.

Jika Anda ingin jenis logika bercabang ini, Anda harus memanggil prosedur yang tersimpan:

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;

Atau SQL dinamis:

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;

Semoga ini membantu!

Erik Darling
sumber