Tiêu chuẩn SQL Server 2012 và 2016: Nếu tôi đặt logic if-other trong một thủ tục được lưu trữ để thực thi một trong hai nhánh mã, tùy thuộc vào giá trị của một tham số, động cơ có lưu trữ phiên bản mới nhất không?
Không, nó lưu trữ tất cả các phiên bản. Hay đúng hơn, nó lưu trữ một phiên bản với tất cả các đường dẫn được khám phá, được biên dịch với các biến được truyền.
Đây là bản demo nhanh, sử dụng cơ sở dữ liệu Stack Overflow.
Tạo một chỉ mục:
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
Tạo một thủ tục được lưu trữ với một gợi ý chỉ mục trỏ đến một chỉ mục không tồn tại, trong mã phân nhánh.
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;
Nếu tôi thực thi lệnh lưu trữ đó đang tìm kiếm Danh tiếng = 1, tôi sẽ gặp lỗi.
EXEC dbo.YourMom @Reputation = 1;
Msg 308, Cấp 16, Trạng thái 1, Quy trình YourMom, Dòng 14 [Dòng bắt đầu hàng loạt 32] Chỉ mục 'ix_yourdad' trên bảng 'dbo.Users' (được chỉ định trong mệnh đề TỪ) không tồn tại.
Nếu chúng tôi sửa tên chỉ mục và chạy lại truy vấn, kế hoạch lưu trữ sẽ giống như sau:
Bên trong, XML sẽ có hai tham chiếu đến @Reputation
biến.
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
Một thử nghiệm đơn giản hơn một chút sẽ chỉ là lấy một kế hoạch ước tính cho các Proc được lưu trữ. Bạn có thể thấy trình tối ưu hóa khám phá cả hai đường dẫn:
Và nếu trong lần thực hiện sau, giá trị của tham số thay đổi, nó sẽ biên dịch lại và lưu lại bộ đệm thủ tục đã lưu, bởi vì một nhánh khác của mã phải được thực thi? (Truy vấn này khá tốn kém để biên dịch.) Cảm ơn bạn.
Không, nó sẽ giữ lại giá trị thời gian chạy của quá trình biên dịch đầu tiên.
Nếu chúng ta thực hiện lại với một khác @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
Từ kế hoạch thực tế :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
Chúng tôi vẫn có giá trị biên dịch là 1, nhưng bây giờ giá trị thời gian là 2.
Trong bộ đệm kế hoạch, bạn có thể kiểm tra bằng một công cụ miễn phí như công cụ của công ty tôi phát triển, sp_BlitzCache :
Thủ tục được lưu trữ đã được gọi hai lần và mỗi câu lệnh trong nó đã được gọi một lần.
Vậy chúng ta có gì? Một kế hoạch lưu trữ cho cả hai truy vấn trong thủ tục được lưu trữ.
Nếu bạn muốn loại logic phân nhánh này, bạn phải gọi các thủ tục được lưu trữ phụ:
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;
Hoặc SQL động:
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;
Hi vọng điêu nay co ich!