Почему запрос работает медленнее в хранимой процедуре, чем в окне запроса?

dba.stackexchange https://dba.stackexchange.com/questions/15262

Вопрос

У меня есть сложный запрос, который работает через 2 секунды в окне запроса, но примерно в 5 минут в качестве хранимой процедуры. Почему нужно работать так же дольше, чтобы работать в качестве хранимой процедуры?

Вот как выглядит мой запрос.

Требуется определенный набор записей (идентифицированным @id а также @createdDate) и конкретный срок (1 год, начиная с @startDate) и возвращает суммированный список писем, отправленных и оценочных платежей, полученных в результате этих писем.

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

Конечный результат выглядит так:

Date        Letter Code     Letters Sent    Amount Paid
1/1/2012    a               1245            12345.67
1/1/2012    b               2301            1234.56
1/1/2012    c               1312            7894.45
1/1/2012    a               1455            2345.65
1/1/2012    c               3611            3213.21

У меня проблемы с выяснением, где находится замедление, потому что все работает очень быстро в редакторе запросов. Только когда я перемещаю запрос в хранимую процедуру, она начинает так долго работать.

Я уверен, что это как -то связано с созданием плана выполнения запросов, но я недостаточно знаю о SQL, чтобы определить, что может вызвать проблему.

Вероятно, следует отметить, что все таблицы, используемые в запросе, имеют миллионы записей.

Может ли кто -нибудь объяснить мне, почему это займет гораздо больше времени, чтобы работать в качестве хранимой процедуры, чем в редакторе запросов, и помочь мне определить, какая часть моего запроса может вызвать проблемы с производительностью при запуске в качестве хранимой процедуры?

Это было полезно?

Решение

В качестве Мартин указал в комментариях, Проблема заключается в том, что запрос использует кэшированный план, который неуместен для приведенных параметров.

Ссылка, на которой он предоставил Медленно в приложении, быстро в SSMS? Понимание загадок производительности предоставил много полезной информации, которая приводит меня к некоторым решениям.

Решение, которое я использую в настоящее время, заключается в том, чтобы скопировать параметры на локальные переменные в процедуре, которая, я думаю, делает SQL переоценить план выполнения для запроса в любое время, когда он работает, поэтому он выбирает лучший план выполнения для приведенных параметров вместо использования неуместный кэшированный план запроса.

Другие решения, которые могут работать, - это использование OPTIMIZE FOR или же RECOMPILE запросы подсказки.

Другие советы

Из аналогичного вопроса на Stackoverflow (с большим количеством ответов), проверьте свою сохраненную процедуру.

  • ПЛОХО: SET ANSI_NULLS OFF (5 минут, 6 -метровая энергичная катушка)
  • ХОРОШИЙ: SET ANSI_NULLS ON (0,5 секунды)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top