Чому запит запускається повільніше у Збереженій процедурі, ніж у вікні Запит?


14

У мене складний запит, який працює за 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

Кінцевий результат виглядає приблизно так:

Код листів Літери, що надсилаються Сума сплачена
01.01.2012 р. 1245 12345.67
01.01.2012 р. 2301 1234.56
01.01.2012 c 1312 7894.45
01.01.2012 р. 1455 2345,65
01.01.2012 c 3611 3213.21

У мене виникають проблеми з розібранням, де знаходиться уповільнення, оскільки в редакторі запитів все працює надзвичайно швидко. Лише коли я переміщую запит на збережену процедуру, він починає так довго працювати.

Я впевнений, що це має щось спільне з генерацією плану виконання запитів, але я не знаю достатньо про SQL, щоб визначити, що може спричинити проблему.

Напевно, слід зазначити, що всі таблиці, використані в запиті, мають мільйони записів.

Чи може хтось пояснити мені, чому це забирає стільки часу, щоб запустити як збережену процедуру, ніж у редакторі запитів, і допоможе мені визначити, яка частина мого запиту може викликати проблеми з ефективністю при запуску як збережена процедура?


@MartinSmith Дякую Я вважаю за краще уникати RECOMPILEпідказки, оскільки я не хочу перекомпілювати запит кожного разу, коли він запускається, а стаття, яку ви пов’язали, згадала, що параметри копіювання на локальну змінну еквівалентні використанню OPTIMIZE FOR UNKNOWN, яке, здається, доступне лише в 2008 р. І пізніше. Я думаю, що зараз я буду дотримуватися копіювання параметрів до локальної змінної, що приводить час мого виконання запитів до 1-2 секунд.
Рейчел

Відповіді:


5

Як зазначив Мартін у коментарях , проблема полягає в тому, що запит використовує кешований план, який не відповідає відповідним параметрам.

Посилання, яке він надав на Slow у програмі, Fast у SSMS? Розуміння таємниць продуктивності дало багато корисної інформації, яка привела мене до деяких рішень.

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

Інші рішення, які можуть працювати, використовують підказки OPTIMIZE FORабо RECOMPILEзапити.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.