Piano di esecuzione diverso durante l'esecuzione dell'istruzione direttamente e dalla stored procedure
-
05-07-2019 - |
Domanda
Durante lo sviluppo di una nuova query al lavoro, l'ho scritta e profilata in SQL Query Analyzer. La query ha funzionato davvero bene senza scansioni di tabelle ma quando l'ho incapsulata in una procedura memorizzata, le prestazioni sono state orribili. Quando ho esaminato il piano di esecuzione ho visto che SQL Server ha scelto un piano diverso che utilizzava una scansione di tabella anziché una ricerca di indice su TableB (sono stato costretto a offuscare un po 'i nomi di tabella e colonna ma nessuna della logica della query è cambiato).
Ecco la query
SELECT
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)) AS Day,
DATEPART(hh, TableA.Created) AS [Hour],
SUM(TableB.Quantity) AS Quantity,
SUM(TableB.Amount) AS Amount
FROM
TableA
INNER JOIN TableB ON TableA.BID = TableB.ID
WHERE
(TableA.ShopId = @ShopId)
GROUP BY
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)),
DATEPART(hh, TableA.Created)
ORDER BY
DATEPART(hh, TableA.Created)
Quando eseguo la query " raw " Ottengo le seguenti statistiche di traccia
Event Class Duration CPU Reads Writes SQL:StmtCompleted 75 41 7 0
E quando eseguo la query come proc memorizzato usando il seguente comando
DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId
Ottengo le seguenti statistiche di traccia
Event Class Duration CPU Reads Writes SQL:StmtCompleted 222 10 48 0
Ottengo lo stesso risultato anche se memorizzo la query in un nvarchar ed eseguo usando sp_executesql in questo modo (funziona come lo sproc)
DECLARE @SQL nvarchar(2000)
SET @SQL = 'SELECT DATEADD(dd, ...'
exec sp_executesql @SQL
La procedura memorizzata non contiene nulla ad eccezione dell'istruzione select sopra. Cosa indurrebbe SQL Server a scegliere un piano di esecuzione inferiore solo perché l'istruzione viene eseguita come procedura memorizzata?
Attualmente siamo in esecuzione su SQL Server 2000
Soluzione
In genere ciò ha a che fare con lo sniffing dei parametri. Può essere molto frustrante da affrontare. A volte può essere risolto ricompilando la procedura memorizzata, e talvolta è anche possibile utilizzare una variabile duplicata all'interno della procedura memorizzata in questo modo:
alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;
E quindi utilizzare @ p1_copy nella query. Sembra ridicolo ma funziona.
Controlla la mia recente domanda sullo stesso argomento:
Perché l'ottimizzatore SqlServer viene così confuso con parametri?
Altri suggerimenti
Sì - l'avevo visto anche su Oracle DB 11g - la stessa query è stata eseguita rapidamente su 2 nodi del server db al prompt SQL MA quando chiamato dal pacchetto ha letteralmente riagganciato!
ha dovuto cancellare il pool condiviso per ottenere un comportamento identico: motivo per cui era in esecuzione un lavoro / script che aveva una copia precedente bloccata nella cache / memoria della libreria su un nodo con piano di esecuzione inferiore.