Index XEMK không được sử dụng trừ khi TÙY CHỌN (RECOMPILE)?


11

(Câu hỏi được chuyển từ SO)

Tôi có một bảng (dữ liệu giả) với chỉ mục được nhóm chứa 2 cột:

nhập mô tả hình ảnh ở đây

Bây giờ tôi chạy hai truy vấn đó:

declare 
@productid int =1 , 
@priceid  int = 1




SELECT productid,
       t.priceID
FROM   Transactions AS t
WHERE  (productID = @productid OR @productid IS NULL)
       AND (priceid = @priceid OR @priceid IS NULL)  


SELECT productid,
       t.priceID
FROM   Transactions AS t
WHERE  (productID = @productid)
       AND (priceid = @priceid)

Kế hoạch thực hiện thực tế cho cả hai truy vấn là:

nhập mô tả hình ảnh ở đây

Như bạn có thể thấy, cái đầu tiên đang sử dụng SCAN trong khi cái thứ hai đang sử dụng XEMK.

Tuy nhiên - thêm OPTION (RECOMPILE)vào truy vấn đầu tiên, thực hiện kế hoạch thực hiện cũng để sử dụng XEMK:

nhập mô tả hình ảnh ở đây

Bạn bè tại DBA chat nói với tôi rằng:

Trong truy vấn của bạn, @ sản phẩm = 1, có nghĩa là (sản phẩmID = @ sản phẩm HOẶC @productID IS NULL) có thể được đơn giản hóa thành (sản phẩmID = @ sản phẩm). Cái trước yêu cầu quét để làm việc với bất kỳ giá trị nào của @productID, cái sau có thể sử dụng tìm kiếm. Vì vậy, khi bạn sử dụng RECOMPILE, SQL Server sẽ xem xét giá trị bạn thực sự có trong @productID và đưa ra kế hoạch tốt nhất cho nó. Với giá trị không null trong @productID, tìm kiếm là tốt nhất. Nếu giá trị của @productID không xác định, gói phải phù hợp với bất kỳ giá trị nào có thể có trong @productID, sẽ yêu cầu quét. Được cảnh báo: TÙY CHỌN (RECOMPILE) sẽ buộc biên dịch lại kế hoạch mỗi khi bạn chạy nó, điều này sẽ thêm một vài mili giây cho mỗi lần thực hiện. Mặc dù đây chỉ là một vấn đề nếu truy vấn chạy rất thường xuyên.

Cũng thế :

Nếu @productID là null, bạn sẽ tìm kiếm giá trị nào? Trả lời: không có gì để tìm kiếm. Tất cả các giá trị đủ điều kiện.

Tôi hiểu rằng OPTION (RECOMPILE)buộc SQL Server phải xem các giá trị thực tế của các tham số là gì và xem liệu nó có thể TÌM KIẾM với nó không.

Nhưng bây giờ tôi mất lợi ích của việc biên soạn trước.

Câu hỏi

IMHO - SCAN sẽ chỉ xảy ra nếu một param là null.
Điều đó tốt - hãy để SQL SERVER tạo một kế hoạch thực hiện cho SCAN.
NHƯNG nếu SQL Server thấy rằng tôi chạy truy vấn này nhiều lần với các giá trị: 1,1vậy thì tại sao nó không tạo ra kế hoạch thực hiện KHÁC và sử dụng XEMK cho điều đó?

AFAIK - SQL tạo kế hoạch thực hiện cho các truy vấn thành công nhất .

  • Tại sao SQL SERVER không lưu kế hoạch thực hiện cho:

    @productid int =1 , @priceid int = 1

(Tôi chạy nó nhiều lần với các giá trị đó)

  • Có thể buộc SQL giữ kế hoạch thực hiện đó (sử dụng XEMK) - cho lệnh gọi trong tương lai không?

Tạo tập lệnh bảng đầy đủ + dữ liệu


Câu trả lời:


10

Tóm tắt một số điểm chính từ cuộc thảo luận trong phòng trò chuyện của chúng tôi :


Nói chung, SQL Server lưu trữ một kế hoạch duy nhất cho mỗi câu lệnh . Kế hoạch đó phải hợp lệ cho tất cả các giá trị tham số có thể trong tương lai .

Không thể lưu trữ kế hoạch tìm kiếm cho truy vấn của bạn, vì kế hoạch đó sẽ không hợp lệ nếu, ví dụ: @productid là null.

Trong một số bản phát hành trong tương lai, SQL Server có thể hỗ trợ một gói duy nhất tự động chọn giữa quét và tìm kiếm, tùy thuộc vào các giá trị tham số thời gian chạy, nhưng đó không phải là thứ chúng ta có ngày hôm nay.

Lớp vấn đề chung

Truy vấn của bạn là một ví dụ về một mẫu được gọi khác nhau là truy vấn "bắt tất cả" hoặc "tìm kiếm động". Có nhiều giải pháp khác nhau, mỗi giải pháp đều có ưu điểm và nhược điểm riêng. Trong các phiên bản hiện đại của SQL Server (2008+), các tùy chọn chính là:

  • IF khối
  • OPTION (RECOMPILE)
  • SQL động sử dụng sp_executesql

Tác phẩm toàn diện nhất về chủ đề này có lẽ là của Erland Sommarskog, được bao gồm trong các tài liệu tham khảo ở cuối câu trả lời này. Không thể tránh khỏi sự phức tạp liên quan, vì vậy cần phải đầu tư một chút thời gian để thử từng lựa chọn để hiểu sự đánh đổi trong từng trường hợp.

IF khối

Để minh họa một IFgiải pháp khối cho trường hợp cụ thể trong câu hỏi:

IF @productid IS NOT NULL AND @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid
        AND T.priceID = @priceid;
END;
ELSE IF @productid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid;
END;
ELSE IF @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.priceID = @priceid;
END;
ELSE
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T;
END;

Điều này chứa một câu lệnh riêng cho bốn trường hợp null-hoặc-không-null có thể có cho mỗi trong hai tham số (hoặc biến cục bộ), do đó có bốn kế hoạch.

Có một vấn đề tiềm ẩn ở đó với việc đánh hơi tham số, có thể yêu cầu một OPTIMIZE FORgợi ý cho mỗi truy vấn. Vui lòng xem phần tài liệu tham khảo để khám phá các loại tinh tế này.

Biên dịch lại

Như đã lưu ý ở trên một câu hỏi, bạn cũng có thể thêm một OPTION (RECOMPILE)gợi ý để có được một kế hoạch mới (tìm kiếm hoặc quét) trên mỗi lời mời. Với tần suất cuộc gọi tương đối chậm trong trường hợp của bạn (trung bình cứ sau mười giây, với thời gian biên dịch dưới một phần nghìn giây), có vẻ như tùy chọn này sẽ phù hợp với bạn:

SELECT
    T.productID,
    T.priceID
FROM dbo.Transactions AS T
WHERE
    (T.productID = @productid OR @productid IS NULL)
    AND (T.priceID = @priceid OR @priceid IS NULL)
OPTION (RECOMPILE);

Cũng có thể kết hợp các tính năng từ các tùy chọn trên theo cách sáng tạo, để tận dụng tối đa các lợi thế của từng phương pháp, đồng thời giảm thiểu các nhược điểm. Thực sự không có lối tắt để hiểu chi tiết về công cụ này, sau đó đưa ra lựa chọn sáng suốt được hỗ trợ bởi thử nghiệm thực tế.

đọc thêm

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.