ストアドプロシージャでのクエリの実行がクエリウィンドウよりも遅いのはなぜですか?


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

最終結果は次のようになります。

日付レターコードレター支払額支払額
1/1/2012 a 1245 12345.67
1/1/2012 b 2301 1234.56
1/1/2012 c 1312 7894.45
1/1/2012 1455 2345.65
1/1/2012 c 3611 3213.21

クエリエディターではすべてが非常に高速に実行されるため、速度低下の原因を特定するのに問題があります。クエリの実行に時間がかかるのは、クエリをストアドプロシージャに移動したときだけです。

クエリの実行プランが生成されることと関係があると確信していますが、問題の原因を特定するためのSQLについては十分に知りません。

クエリで使用されるすべてのテーブルには数百万のレコードがあることに注意してください。

誰かがこれがクエリエディタよりもストアドプロシージャとして実行するのに非常に時間がかかる理由を説明でき、ストアドプロシージャとして実行したときにクエリのどの部分がパフォーマンスの問題を引き起こしているのかを特定するのに役立ちますか?


@MartinSmithありがとう。RECOMPILEクエリを実行するたびにクエリを再コンパイルしたくないので、ヒントを避けることをお勧めします。リンク先の記事では、ローカル変数にパラメータをコピーすることはusing OPTIMIZE FOR UNKNOWNのみと同等であると述べています。 2008以降。ここでは、パラメータをローカル変数にコピーすることに固執すると思います。これにより、クエリの実行時間が1〜2秒に戻ります。
レイチェル

回答:


5

以下のようマーティンはコメントで指摘し、問題は、クエリが与えられたパラメータに不適切なキャッシュされたプランを使用していることです。

がアプリケーションで遅い、SSMSで速いで提供したリンクパフォーマンスミステリーを理解することは、いくつかの解決策につながる多くの有用な情報を提供しました。

私が現在使用している解決策は、パラメータをプロシージャ内のローカル変数にコピーすることです。これにより、SQLは実行されるたびにクエリの実行計画を再評価し、使用する代わりに指定されたパラメータの最適な実行計画を選択しますクエリの不適切なキャッシュプラン。

動作する可能性のある他のソリューションは、OPTIMIZE FORまたはRECOMPILEクエリヒントを使用しています。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.