Ho una query complessa che viene eseguita in 2 secondi nella finestra della query, ma circa 5 minuti come Stored Procedure. Perché l'esecuzione di una procedura memorizzata richiede molto più tempo?
Ecco come appare la mia domanda.
Prende un set specifico di record (identificato da @id
e @createdDate
) e un intervallo di tempo specifico (1 anno a partire da @startDate
) e restituisce un elenco riepilogativo di lettere inviate e pagamenti stimati ricevuti a seguito di tali lettere.
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
Il risultato finale è simile al seguente:
Data Lettera Codice Lettere Importo inviato pagato 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
Ho problemi a capire dove si trova il rallentamento, perché tutto funziona estremamente velocemente nell'editor di query. È solo quando sposto la query in una stored procedure che inizia a impiegare così tanto tempo per l'esecuzione.
Sono sicuro che abbia qualcosa a che fare con la generazione del piano di esecuzione della query, ma non so abbastanza su SQL per identificare ciò che potrebbe causare il problema.
Va probabilmente notato che tutte le tabelle utilizzate nella query hanno milioni di record.
Qualcuno può spiegarmi perché l'esecuzione di questa procedura richiede molto più tempo rispetto all'editor di query e può aiutarmi a identificare quale parte della mia query potrebbe causare problemi di prestazioni quando eseguita come procedura memorizzata?
RECOMPILE
suggerimento poiché non voglio davvero ricompilare la query ogni volta che viene eseguita e l'articolo che hai collegato menzionava che copiare i parametri in una variabile locale è l'equivalente come usareOPTIMIZE FOR UNKNOWN
, che sembra essere disponibile solo in 2008 e successive. Penso che per ora continuerò a copiare i parametri in una variabile locale, il che riduce i tempi di esecuzione della query a 1-2 secondi.