2012-06-20 11 views
8

Tôi có một quy trình được lưu trữ thực hiện khủng khiếp. Khi tôi khai báo một biến, đặt giá trị của nó và sau đó sử dụng nó trong mệnh đề where câu lệnh mất hơn một giờ để chạy. Khi tôi mã cứng các biến trong mệnh đề where nó chạy trong chưa đầy một giây.Tham số không hoạt động cũng như mã hóa cứng giá trị

Tôi bắt đầu xem xét những gì đã xảy ra với thông qua kế hoạch thực hiện. Có vẻ như khi tôi thử và chuyển nó một số biến đã khai báo, kế hoạch thực thi sẽ gộp một số Hash Match vì nó chọn giá trị từ một khung nhìn sử dụng một UNION và một biểu thức bảng chung.

 
/************* Begin of Stored Procedure ***************/ 
CREATE PROCEDURE GetFruit 
    @ColorId bigint, 
    @SeasionId bigint 
WITH RECOMPILE 
AS 
BEGIN 

SELECT 
    A.Name 
FROM 
    [Apple_View] A /* This is the view down below */ 
    INNER JOIN [Fruit] F 
     ON (F.ColorId = @ColorId 
      AND A.FruitId = F.FruitId)   
WHERE 
    (A.ColorId = @ColorId 
    AND 
    A.SeasonId = @SeasonId) 

END 
/************* End of Stored Procedure ***************/ 

/************* Begin of View ***************/ 
WITH Fruits (FruitId, ColorId, SeasonId) AS 
(
    -- Anchor member 
    SELECT 
     F.FruitId 
     ,F.ColorId 
     ,F.SeasonId 
    FROM 
     (( 
      SELECT DISTINCT 
       EF.FruitId 
       ,EF.ColorId 
       ,EF.SeasonId 
       ,EF.ParentFruitId 
      FROM 
       ExoticFruit EF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = EF.FruitId 
     UNION 
      SELECT DISTINCT 
       SF.FruitId 
       ,SF.ColorId 
       ,SF.SeasonId 
       ,SF.ParentFruitId    
      FROM 
       StinkyFruit SF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = SF.FruitId 
     UNION 
      SELECT DISTINCT 
       CF.FruitId 
       ,CF.ColorId 
       ,CF.SeasonId 
       ,CF.ParentFruitId 
      FROM 
       CrazyFruit CF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = CF.FruitId 

      )) f 

    UNION ALL 

    -- Recursive Parent Fruit 
    SELECT 
     FS.FruitId 
     ,FS.ColorId 
     ,FS.SeasonId 
     ,FS.ParentFruitId 
    FROM 
     Fruits FS 
     INNER JOIN MasterFruit MF 
      ON MF.[ParentFruitId] = fs.[FruitId] 
) 

SELECT DISTINCT 
    FS.FruitId 
    ,FS.ColorId 
    ,FS.SeasonId 
    FROM 
     Fruits FS 

/************* End of View ***************/ 


/* To Execute */ 
EXEC GetFruit 1,3 

Nếu tôi chạy Stored Procedure sử dụng các giá trị thiết lập phải mất hơn một giờ và ở đây là kế hoạch thực hiện. With Variables

Nếu tôi chạy Stored Procedure loại bỏ các giá trị DECLARE và SET và chỉ cần thiết lập các khoản đâu tuyên bố sau nó chạy trong vòng chưa đầy một giây và đây là kế hoạch thực hiện:

WHERE(A.ColorId = 1 AND A.SeasonId = 3) 

hard coded where clause

Lưu ý cách biến được mã hóa cứng sử dụng lập chỉ mục trong khi biến đầu tiên sử dụng tập hợp băm. Tại sao vậy? Tại sao các giá trị mã hóa cứng trong mệnh đề where hoạt động khác với các biến được khai báo?

------- đây là những gì cuối cùng thực hiện với sự giúp đỡ của @ user1166147 ------

Tôi đã thay đổi thủ tục lưu trữ để sử dụng sp_executesql.

 
CREATE PROCEDURE GetFruit 
    @ColorId bigint, 
    @SeasionId bigint 
WITH RECOMPILE 
AS 
BEGIN 

DECLARE @SelectString nvarchar(max) 

SET @SelectString = N'SELECT 
    A.Name 
FROM 
    [Apple_View] A /* This is the view down below */ 
    INNER JOIN [Fruit] F 
     ON (F.ColorId = @ColorId 
      AND A.FruitId = F.FruitId)   
WHERE 
    (A.ColorId = ' + CONVERT(NVARCHAR(MAX), @ColorId) + ' 
    AND 
    A.SeasonId = ' + CONVERT(NVARCHAR(MAX), @SeasonId) + ')' 

EXEC sp_executesql @SelectString 

END 
+0

Bạn đã kiểm tra để đảm bảo các loại tham số dữ liệu của bạn phù hợp với kiểu dữ liệu cột? – HackedByChinese

+2

Đây là các biến không phải là tham số. SQL Server không làm thay đổi độ lệch để ước tính chọn lọc sẽ được đoán. Nếu bạn thêm 'OPTION (RECOMPILE)' thì sao? –

+1

sử dụng tùy chọn biên dịch lại. Có cái gì đó gọi là tham số sniffing trong đó máy chủ sql tạo ra kế hoạch truy vấn khác nhau tùy thuộc vào giá trị đầu vào – praveen

Trả lời

2

EDIT TÓM TẮT mỗi một yêu cầu từ Damien_The_Unbeliever

Mục đích là để có được thông tin tốt nhất/nhất về giá trị biến to SQL TRƯỚC kế hoạch được tạo ra, nói chung sniffing tham số thực hiện điều này. Có thể có lý do khiến thông số đánh hơi bị 'tắt' trong trường hợp này. Nếu không nhìn thấy một biểu diễn tốt hơn về mã thực tế, chúng tôi không thể thực sự nói giải pháp là gì hoặc tại sao vấn đề tồn tại. Hãy thử những điều dưới đây để buộc các khu vực bị ảnh hưởng tạo các gói bằng cách sử dụng các giá trị thực tế.

* DÀI VERSION VỚI CHI TIẾT THÊM *

Đây có phải là proc lưu trữ thực tế của bạn? Bạn có các giá trị mặc định cho tham số của mình không? Nếu vậy, chúng là gì?

Thông số sniffing có thể giúp - nhưng nó phải có giá trị tham số điển hình để tạo kế hoạch tốt và nếu không, sẽ không thực sự giúp hoặc tạo kế hoạch xấu dựa trên giá trị tham số không điển hình. Vì vậy, nếu một biến có giá trị mặc định là null hoặc giá trị không phải là giá trị điển hình thì lần đầu tiên nó được chạy và kế hoạch được biên dịch - nó tạo ra một kế hoạch xấu.

Nếu ai đó đã viết sproc này - họ có thể có cố ý 'vô hiệu hóa' tham số sniffing với các biến địa phương vì một lý do. Quy tắc kinh doanh có thể yêu cầu những cấu trúc biến đổi này.

Mục đích là để có được thông tin tốt nhất/hầu hết về giá trị biến cho SQL TRƯỚC KHI kế hoạch được tạo ra và thông thường Sniffing Parameter thực hiện điều này. Nhưng có những thứ có thể làm cho nó ảnh hưởng đến hiệu suất tiêu cực, và đó có thể là lý do tại sao nó bị 'vô hiệu hoá'.Nó vẫn có vẻ như kế hoạch đang được tạo ra với các giá trị không điển hình cho các tham số hoặc không đủ thông tin vẫn còn - sử dụng tham số sniffing hay không.

Hãy thử gọi truy vấn bên trong sproc với Sử dụng sp_executesql để thực thi các truy vấn bị ảnh hưởng, buộc nó tạo kế hoạch cho vùng đó bằng các biến thực tế và xem liệu nó có tốt hơn không. Đây có thể là giải pháp của bạn nếu bạn phải có loại giá trị tham số bất thường này - tạo các procs được lưu trữ để chạy các phần bị ảnh hưởng và gọi chúng sau này từ bên trong thủ tục được lưu trữ - sau khi biến đã nhận được một giá trị điển hình.

Nếu không thấy sự thể hiện tốt hơn về mã thực tế, thật khó để xem vấn đề là gì. Hy vọng rằng thông tin này sẽ giúp -

+0

Trong khi bạn đang ở trong một tâm trạng chỉnh sửa - tại thời điểm này, đây chỉ là một bức tường văn bản. Có thể thử phá vỡ nó thành các đoạn văn? –

+0

Nó không phải là thủ tục được lưu trữ thực tế. Nó chính xác 95%, nó chỉ có các thay đổi tên cột để giữ cho các tên bảng và các cột riêng tư kể từ khi nó là một hệ thống tài chính. * Tôi biết nó hút và làm cho nó khó hơn. Tôi xin lỗi tôi đã phải làm điều đó, nhưng đã làm hết sức mình để làm cho nó gần như tôi có thể với những thay đổi tối thiểu – Mark

+0

Tôi đã có thể làm cho nó để thực hiện trong vòng chưa đầy một giây bằng cách sử dụng 'sp_executesql'. Tôi sẽ đưa giải pháp lên trong câu hỏi của tôi cho người dùng trong tương lai. – Mark

0

Bạn có thể buộc nó để tối ưu hóa truy vấn của bạn dựa trên các giá trị tiêu biểu mà bạn có thể biết rõ hơn. Để truy vấn ban đầu của bạn thêm dòng sau:

OPTION (OPTIMIZE FOR(@ColorId = 1, @SeasionId = 3)) 

Nó sẽ có tác dụng tương tự mà không cần SQL động. Nếu bạn không biết những giá trị tiêu biểu, bạn có thể thực hiện tối ưu hóa sniffing họ:

OPTION (OPTIMIZE FOR UNKNOWN) 

Một lần nữa, không có SQL động