Tại sao một truy vấn chạy chậm hơn trong Thủ tục lưu trữ hơn trong cửa sổ Truy vấn?


14

Tôi có một truy vấn phức tạp chạy trong 2 giây trong cửa sổ truy vấn, nhưng khoảng 5 phút dưới dạng Thủ tục được lưu trữ. Tại sao phải mất quá nhiều thời gian để chạy như một thủ tục được lưu trữ?

Đây là những gì truy vấn của tôi trông như thế nào.

Nó lấy một bộ hồ sơ cụ thể (được xác định bởi @id@createdDate), và khung thời gian cụ thể (bắt đầu từ 1 năm @startDate) và trả về một danh sách tóm tắt các thư được gửi và các khoản thanh toán ước tính nhận được do kết quả của các thư đó.

CREATE PROCEDURE MyStoredProcedure
    @id int,
    @createdDate varchar(20),
    @startDate varchar(20)

 AS
SET NOCOUNT ON

    -- Get the number of records * .7
    -- Only want to return records containing letters that were sent on 70% or more of the records
    DECLARE @limit int
    SET @limit = IsNull((SELECT Count(*) FROM RecordsTable WITH (NOLOCK) WHERE ForeignKeyId = @id AND Created = @createdDate), 0) * .07

    SELECT DateSent as [Date] 
        , LetterCode as [Letter Code]
        , Count(*) as [Letters Sent]
        , SUM(CASE WHEN IsNull(P.DatePaid, '1/1/1753') BETWEEN DateSent AND DateAdd(day, 30, DateSent) THEN IsNull(P.TotalPaid, 0) ELSE 0 END) as [Amount Paid]
    INTO #tmpTable
    FROM (

        -- Letters Table. Filter for specific letters
        SELECT DateAdd(day, datediff(day, 0, LR.DateProcessed), 0) as [DateSent] -- Drop time from datetime
            , LR.LetterCode -- Letter Id
            , M.RecordId -- Record Id
        FROM LetterRequest as LR WITH (NOLOCK)
        INNER JOIN RecordsTable as M WITH (NOLOCK) ON LR.RecordId = M.RecordId
        WHERE ForeignKeyId = @id AND Received = @createdDate
            AND LR.Deleted = 0 AND IsNull(LR.ErrorDescription, '') = ''
            AND LR.DateProcessed BETWEEN @startDate AND DateAdd(year, 1, @startDate)
            AND LR.LetterCode IN ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o')
    ) as T
    LEFT OUTER JOIN (

        -- Payment Table. Payments that bounce are entered as a negative payment and are accounted for
        SELECT PH.RecordId, PH.DatePaid, PH.TotalPaid
        FROM PaymentHistory as PH WITH (NOLOCK)
            INNER JOIN RecordsTable as M WITH (NOLOCK) ON PH.RecordId = M.RecordId
            LEFT OUTER JOIN PaymentHistory as PR WITH (NOLOCK) ON PR.ReverseOfUId = PH.UID
        WHERE PH.SomeString LIKE 'P_' 
            AND PR.UID is NULL 
            AND PH.DatePaid BETWEEN @startDate AND DateAdd(day, 30, DateAdd(year, 1, @startDate))
            AND M.ForeignKeyId = @id AND M.Created = @createdDate
    ) as P ON T.RecordId = P.RecordId

    GROUP BY DateSent, LetterCode
    --HAVING Count(*) > @limit
    ORDER BY DateSent, LetterCode

    SELECT *
    FROM #tmpTable
    WHERE [Letters Sent] > @limit

    DROP TABLE #tmpTable

Kết quả cuối cùng trông như thế này:

Mã thư ngày Thư đã gửi Số tiền phải trả
1/1/2012 a 1245 12345,67
1/1/2012 b 2301 1234.56
1/1/2012 c 1312 7894,45
1/1/2012 một 1455 2345,65
1/1/2012 c 3611 3213,21

Tôi đang gặp vấn đề khi tìm ra sự chậm lại ở đâu, bởi vì mọi thứ chạy rất nhanh trong trình soạn thảo truy vấn. Chỉ khi tôi di chuyển truy vấn đến một thủ tục được lưu trữ thì nó mới bắt đầu mất quá nhiều thời gian để chạy.

Tôi chắc chắn rằng nó có liên quan đến kế hoạch thực hiện truy vấn được tạo, nhưng tôi không biết đủ về SQL để xác định điều gì có thể gây ra sự cố.

Có lẽ cần lưu ý rằng tất cả các bảng được sử dụng trong truy vấn có hàng triệu bản ghi.

Ai đó có thể giải thích cho tôi tại sao việc này lại mất nhiều thời gian để chạy như một thủ tục được lưu trữ hơn trong trình soạn thảo truy vấn không và giúp tôi xác định phần nào trong truy vấn của tôi có thể gây ra sự cố về hiệu suất khi chạy như một thủ tục được lưu trữ?


@MartinSmith Cảm ơn. Tôi muốn tránh RECOMPILEgợi ý vì tôi không thực sự muốn biên dịch lại truy vấn mỗi khi nó chạy và bài viết bạn liên kết đã đề cập rằng sao chép tham số vào một biến cục bộ là tương đương với việc sử dụng OPTIMIZE FOR UNKNOWN, dường như chỉ có sẵn trong 2008 và sau đó. Tôi nghĩ bây giờ tôi sẽ gắn bó với việc sao chép các tham số vào một biến cục bộ, điều này khiến thời gian thực hiện truy vấn của tôi giảm xuống còn 1-2 giây.
Rachel

Câu trả lời:


5

Như Martin đã chỉ ra trong các bình luận , vấn đề là truy vấn đang sử dụng gói được lưu trong bộ nhớ cache không phù hợp với các tham số đã cho.

Liên kết anh cung cấp trên Slow in the Application, Fast in SSMS? Hiểu về Bí ẩn Hiệu suất đã cung cấp rất nhiều thông tin hữu ích dẫn tôi đến một số giải pháp.

Giải pháp tôi hiện đang sử dụng là sao chép các tham số vào các biến cục bộ trong quy trình, mà tôi nghĩ làm cho SQL đánh giá lại kế hoạch thực hiện cho truy vấn bất cứ khi nào nó chạy, vì vậy nó chọn kế hoạch thực hiện tốt nhất cho các tham số được đưa ra thay vì sử dụng một kế hoạch lưu trữ không phù hợp cho truy vấn.

Các giải pháp khác có thể hoạt động đang sử dụng OPTIMIZE FORhoặc RECOMPILEgợi ý truy vấn.


0

Từ một câu hỏi tương tự trên Stackoverflow ( có nhiều câu trả lời hơn ), hãy kiểm tra quy trình được lưu trữ của bạn.

  • BAD :SET ANSI_NULLS OFF (5 phút, spool háo hức 6M)
  • TỐT :SET ANSI_NULLS ON (0,5 giây)
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.