저장 프로 시저에서 쿼리가 쿼리 창보다 느리게 실행되는 이유는 무엇입니까?


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

최종 결과는 다음과 같습니다.

날짜 문자 코드 문자 보낸 금액 지불
2012 년 1 월 1 일 1245 12345.67
2012 년 1 월 1 일 b 2301 1234.56
2012 년 1 월 1 일 c 1312 7894.45
2012 년 1 월 1 일 1455 2345.65
2012 년 1 월 1 일 c 3611 3213.21

쿼리 편집기에서 모든 것이 매우 빠르게 실행되기 때문에 속도 저하 위치를 파악하는 데 문제가 있습니다. 쿼리를 저장 프로 시저로 이동하여 실행하는 데 시간이 오래 걸리는 경우에만 가능합니다.

쿼리 실행 계획이 생성되는 것과 관련이 있다고 확신하지만 SQL에 대해 문제를 일으키는 원인을 식별하기에 충분하지 않습니다.

쿼리에 사용 된 모든 테이블에는 수백만 개의 레코드가 있습니다.

누군가 쿼리 편집기에서보다 저장 프로 시저로 실행하는 데 시간이 오래 걸리는 이유를 설명하고 저장 프로 시저로 실행할 때 성능 문제를 일으킬 수있는 쿼리의 일부를 식별 할 수 있습니까?


@MartinSmith 감사합니다. RECOMPILE쿼리가 실행될 때마다 다시 컴파일하고 싶지 않기 때문에 힌트 를 피하고 싶습니다. 링크 된 기사는 매개 변수를 로컬 변수에 복사하는 것이 사용하는 것과 동일하다고 언급 OPTIMIZE FOR UNKNOWN했습니다. 2008 이상. 지금은 매개 변수를 로컬 변수에 복사하여 쿼리 실행 시간을 1-2 초로 줄이겠다 고 생각합니다.
Rachel

답변:


5

으로 마틴 의견에서 지적 , 문제는 쿼리가 주어진 매개 변수에 부적절한 캐시 된 계획을 사용하고 있다는 점이다.

그가 응용 프로그램 에서 느리게 제공 한 링크 , SSMS에서 빠름? 성능 미스터리를 이해 하면 몇 가지 유용한 정보를 얻을 수있었습니다.

현재 사용중인 솔루션은 매개 변수를 프로 시저의 로컬 변수에 복사하는 것입니다. 이는 SQL이 실행될 때마다 쿼리에 대한 실행 계획을 다시 평가하게하므로 사용하는 대신 지정된 매개 변수에 가장 적합한 실행 계획을 선택합니다. 쿼리에 대한 부적절한 캐시 계획.

작동하는 다른 솔루션은 OPTIMIZE FOR또는 RECOMPILE힌트 힌트를 사용하는 것 입니다.


0

Stackoverflow에 대한 비슷한 질문 ( 응답이 더 있음 )에서 저장 프로 시저를 확인하십시오.

  • 나쁨 :SET ANSI_NULLS OFF (5 분, 6M 열망하는 스풀)
  • GOOD :SET ANSI_NULLS ON (0.5 초)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.