SQL Server - ถ้าลอจิกในขั้นตอนการจัดเก็บและแคชแผน


15

มาตรฐาน SQL Server 2012 และ 2016:

ถ้าฉันใส่if-elseลอจิกในโพรซีเดอร์ที่เก็บไว้เพื่อรันโค้ดหนึ่งในสองสาขาขึ้นอยู่กับค่าของพารามิเตอร์เอ็นจินแคชจะเป็นเวอร์ชันล่าสุดหรือไม่?

และถ้าในการประมวลผลต่อไปนี้ค่าของพารามิเตอร์จะเปลี่ยนแปลงมันจะทำการคอมไพล์และแคชกระบวนการที่เก็บไว้ใหม่อีกครั้งเนื่องจากจะต้องทำการประมวลผลรหัสสาขาอื่นหรือไม่ (แบบสอบถามนี้ค่อนข้างแพงในการรวบรวม)

คำตอบ:


27

SQL Server 2012 และ 2016 มาตรฐาน: ถ้าฉันใส่ตรรกะ if-else ในกระบวนงานที่เก็บไว้เพื่อเรียกใช้โค้ดหนึ่งในสองสาขาขึ้นอยู่กับค่าพารามิเตอร์พารามิเตอร์เอ็นจินแคชเป็นเวอร์ชันล่าสุดหรือไม่

ไม่แคชทุกรุ่น หรือมากกว่านั้นจะเก็บรุ่นหนึ่งโดยมีการสำรวจเส้นทางทั้งหมดรวบรวมด้วยตัวแปรที่ส่งผ่าน

นี่คือตัวอย่างด่วนโดยใช้ฐานข้อมูล Stack Overflow

สร้างดัชนี:

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;

ถ้าฉันเรียกใช้ proc ที่เก็บไว้นั้นกำลังมองหา Reputation = 1 ฉันจะได้รับข้อผิดพลาด

EXEC dbo.YourMom @Reputation = 1;

ข่าวสารเกี่ยวกับ 308, ระดับ 16, สถานะ 1, ขั้นตอน YourMom, บรรทัดที่ 14 [Batch Start Line 32] ดัชนี 'ix_yourdad' บนตาราง 'dbo.Users' (ไม่มีอยู่ในส่วนคำสั่ง FROM)

หากเราแก้ไขชื่อดัชนีและเรียกใช้แบบสอบถามอีกครั้งแผนแคชจะมีลักษณะดังนี้:

ถั่ว

ภายใน XML จะมีการอ้างอิงสอง@Reputationตัวแปร

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

การทดสอบที่ง่ายกว่าเล็กน้อยคือเพียงแค่ได้รับการประมาณแผนสำหรับ proc ที่เก็บไว้ คุณสามารถดูเครื่องมือเพิ่มประสิทธิภาพสำรวจทั้งสองเส้นทาง:

ถั่ว

และถ้าในการดำเนินการต่อไปนี้ค่าของพารามิเตอร์จะเปลี่ยนแปลงมันจะรวบรวมและแคชกระบวนการที่เก็บไว้อีกครั้งเพราะสาขาที่แตกต่างกันของรหัสจะต้องดำเนินการหรือไม่ (แบบสอบถามนี้มีราคาค่อนข้างสูงในการรวบรวม) ขอบคุณ

ไม่มันจะเก็บค่ารันไทม์ของการคอมไพล์ครั้งแรก

หากเราเรียกใช้งานอีกครั้งด้วย@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;

หวังว่านี่จะช่วยได้!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.