it-swarm-eu.dev

Warum wird eine Abfrage in einer gespeicherten Prozedur langsamer ausgeführt als im Abfragefenster?

Ich habe eine komplexe Abfrage, die in 2 Sekunden im Abfragefenster ausgeführt wird, aber ungefähr 5 Minuten als gespeicherte Prozedur. Warum dauert die Ausführung als gespeicherte Prozedur so viel länger?

So sieht meine Anfrage aus.

Es werden bestimmte Datensätze benötigt (gekennzeichnet durch @id und @createdDate) und einen bestimmten Zeitrahmen (1 Jahr ab @startDate) und gibt eine zusammengefasste Liste der gesendeten Briefe und geschätzten Zahlungen zurück, die als Ergebnis dieser Briefe eingegangen sind.

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

Das Endergebnis sieht folgendermaßen aus:

 Datum Brief Code Briefe gesendet Betrag bezahlt 
 01.01.2012 a 1245 12345.67 
 01.01.2012 b 2301 1234.56 
 01.01.2012 c 1312 7894.45 
 01.01.2012 a 1455 2345.65 
 01.01.2012 c 3611 3213.21 

Ich habe Probleme herauszufinden, wo die Verlangsamung liegt, da im Abfrageeditor alles extrem schnell läuft. Erst wenn ich die Abfrage in eine gespeicherte Prozedur verschiebe, dauert die Ausführung so lange.

Ich bin sicher, dass dies etwas mit dem generierten Abfrageausführungsplan zu tun hat, aber ich weiß nicht genug über SQL, um festzustellen, was das Problem verursachen könnte.

Es sollte wahrscheinlich beachtet werden, dass alle in der Abfrage verwendeten Tabellen Millionen von Datensätzen enthalten.

Kann mir jemand erklären, warum die Ausführung als gespeicherte Prozedur so viel länger dauert als im Abfrageeditor, und mir helfen, festzustellen, welcher Teil meiner Abfrage Leistungsprobleme verursachen kann, wenn sie als gespeicherte Prozedur ausgeführt wird?

14
Rachel

Wie Martin hat in den Kommentaren darauf hingewiesen , besteht das Problem darin, dass die Abfrage einen zwischengespeicherten Plan verwendet, der für die angegebenen Parameter ungeeignet ist.

Der Link, den er unter Langsam in der Anwendung, Schnell in SSMS? Grundlegendes zu Leistungsgeheimnissen bereitstellte, lieferte viele nützliche Informationen, die mich zu einigen Lösungen führten.

Die Lösung, die ich derzeit verwende, besteht darin, die Parameter in lokale Prozeduren in der Prozedur zu kopieren. Ich denke, dass SQL den Ausführungsplan für die Abfrage bei jeder Ausführung neu bewertet, sodass der beste Ausführungsplan für die angegebenen Parameter ausgewählt wird, anstatt ihn zu verwenden Ein unangemessener zwischengespeicherter Plan für die Abfrage.

Andere Lösungen, die möglicherweise funktionieren, verwenden OPTIMIZE FOR oder RECOMPILE Abfragehinweise.

5
Rachel

Überprüfen Sie anhand einer ähnlichen Frage zu Stackoverflow ( mit weiteren Antworten ) Ihre gespeicherte Prozedur.

  • [~ # ~] schlecht [~ # ~]: SET ANSI_NULLS OFF (5 Minuten, 6M eifrige Spule)
  • [~ # ~] gut [~ # ~]: SET ANSI_NULLS ON (0,5 Sekunden)
0
Ian Boyd