Perché una query eseguita più lento in una stored procedure che nella finestra Query?
-
16-10-2019 - |
Domanda
Ho una domanda complessa che viene eseguito in 2 secondi nella finestra di query, ma circa 5 minuti come una stored procedure. Perché ci mette così tanto più tempo per eseguire una stored procedure?
Ecco quello che il mio aspetto di query piace.
Ci vuole un insieme specifico di record (identificato da @id
e @createdDate
), e un arco di tempo determinato (1 anno a partire dalla @startDate
) e restituisce un elenco riepilogativo delle lettere inviate e acconti ricevuti a seguito di quelle 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
Gli sguardi risultato finale come questo:
Date Letter Code Letters Sent Amount Paid 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 il rallentamento è, perché tutto fili estremamente veloce nell'editor di query. E 'solo quando mi muovo la query per una stored procedure che si inizia a prendere così tanto tempo per l'esecuzione.
Sono sicuro che abbia qualcosa a che fare con il piano di esecuzione di query sempre generato, ma io non ne so abbastanza su SQL per individuare quello che potrebbe essere la causa del problema.
Si dovrebbe probabilmente essere notato che tutte le tabelle utilizzate nella query hanno milioni di record.
Can qualcuno mi spieghi perché questo sta prendendo molto più tempo per essere eseguito come stored procedure che in l'editor di query, e mi aiuta a identificare quale parte della mia domanda potrebbe essere la causa dei problemi di prestazioni quando viene eseguito come una stored procedure?
Soluzione
Come Martin ha sottolineato nei commenti , il problema è che la query utilizza un piano memorizzato nella cache che è inappropriato per i parametri indicati.
Il collegamento ha fornito su lento nella Domanda, veloce in SSMS? La comprensione delle prestazioni Misteri fornito un sacco di informazioni utili, che mi ha portato ad alcune soluzioni.
La soluzione Attualmente sto usando è quello di copiare i parametri alle variabili locali nella procedura, che credo rende SQL rivalutare il piano di esecuzione per la query in qualsiasi momento è gestito, in modo che raccoglie il miglior piano di esecuzione per i parametri indicati invece di utilizzare una cache piano inadeguato per la query.
Altre soluzioni che potrebbero funzionare utilizzano i OPTIMIZE FOR
o RECOMPILE
hint per la query.
Altri suggerimenti
Da una domanda simile su StackOverflow ( con più risposte ), controllare la stored procedure.
- BAD :
SET ANSI_NULLS OFF
(5 minuti, 6M spool desiderosi) - BUONA :
SET ANSI_NULLS ON
(0,5 secondi)