Il piano di esecuzione della query di SQL Server mostra un conteggio delle righe errato & # 8220; & # 8221; su un indice usato e le prestazioni sono terribilmente lente

StackOverflow https://stackoverflow.com/questions/225309

Domanda

Oggi mi sono imbattuto in un interessante problema di prestazioni con una procedura memorizzata in esecuzione su SQL Server 2005 SP2 in un db in esecuzione su un livello compatibile di 80 (SQL2000).

Il proc dura circa 8 minuti e il piano di esecuzione mostra l'uso di un indice con un conteggio di righe effettivo di 1.339.241.423 che è circa il fattore 1000 superiore al "reale" conteggio delle righe effettivo della tabella stessa che è 1.144.640, come mostrato correttamente dal conteggio delle righe stimato. Quindi il conteggio delle righe effettivo fornito dall'ottimizzatore del piano di query è decisamente errato!

alt text

È interessante notare che quando copio i valori dei parametri procs all'interno del proc su variabili locali e poi utilizzo le variabili locali nella query effettiva, tutto funziona bene - il proc funziona per 18 secondi e il piano di esecuzione mostra il conteggio delle righe effettive corretto.

EDIT: Come suggerito da TrickyNixon, questo sembra essere un segno del problema dello sniffing dei parametri. Ma in realtà, in entrambi i casi ottengo esattamente lo stesso piano di esecuzione. Gli stessi indici vengono utilizzati nello stesso ordine. L'unica differenza che vedo è il modo in cui il conteggio effettivo delle righe nell'indice PK_ED_Transitions è elevato quando si utilizzano direttamente i parametri.

Ho già eseguito dbcc dbreindex e UPDATE STATISTICS senza successo. dbcc show_statistics mostra anche buoni dati per l'indice.

Il proc viene creato CON RECOMPILE, quindi ogni volta che esegue un nuovo piano di esecuzione viene compilato.

Per essere più specifici, questo funziona veloce:

CREATE  Proc [dbo].[myProc](
@Param datetime
)
WITH RECOMPILE 
as

set nocount on

declare @local datetime
set @local = @Param

select 
some columns
from 
table1
where
column = @local
group by
some other columns

E questa versione funziona in modo terribilmente lento, ma produce esattamente lo stesso piano di esecuzione (oltre al conteggio delle righe effettivo troppo elevato su un indice utilizzato):

CREATE  Proc [dbo].[myProc](
@Param datetime
)
WITH RECOMPILE 
as

set nocount on

select 
some columns
from 
table1
where
column = @Param
group by
some other columns

Qualche idea? Qualcuno là fuori che sa da dove Sql Server ottiene il valore effettivo del conteggio delle righe durante il calcolo dei piani di query?

Aggiorna : ho provato la query su un altro server con modalità copat impostata su 90 (Mq2005). È lo stesso comportamento. Penso che aprirò una chiamata di supporto ms, perché mi sembra un bug.

È stato utile?

Soluzione

Ok, finalmente ci sono arrivato da solo.

I due piani di query sono diversi in un piccolo dettaglio che all'inizio mi mancava. quello lento usa un operatore di cicli nidificati per unire due sottoquery. Ciò si traduce in un numero elevato al conteggio delle righe corrente sull'operatore di scansione dell'indice, che è semplicemente il risultato della moltiplicazione del numero di righe dell'input a con il numero di righe dell'input b.

Non so ancora perché l'ottimizzatore decida di utilizzare i loop nidificati anziché una corrispondenza hash che esegue 1000 timer più velocemente in questo caso, ma potrei gestire il mio problema creando un nuovo indice, in modo che il motore esegua un index cerca statt invece di una scansione dell'indice sotto i cicli nidificati.

Altri suggerimenti

Quando si verificano i piani di esecuzione del proc memorizzato rispetto alla query copia / incolla, si utilizzano i piani stimati o i piani effettivi? Assicurarsi di fare clic su Query, Includi piano di esecuzione e quindi eseguire ogni query. Confronta questi piani e vedi quali sono le differenze.

Sembra un caso di Parameter Sniffing. Ecco una spiegazione eccellente insieme a possibili soluzioni: I Smell a Parameter!

Ecco un altro thread StackOverflow che lo risolve: Parameter Sniffing (o Spoofing) in SQL Server

Per me sembra ancora che le statistiche siano errate. La ricostruzione degli indici non li aggiorna necessariamente.

Hai già provato un AGGIORNAMENTO STATISTICO per le tabelle interessate?

Hai eseguito sp_spaceused per verificare se SQL Server ha il riepilogo corretto per quella tabella? Credo che in SQL 2000 il motore usasse quel tipo di metadati durante la costruzione di piani di esecuzione. Prima dovevamo eseguire AGGIORNAMENTO DBCC settimanalmente per aggiornare i metadati su alcune delle tabelle in rapida evoluzione, poiché SQL Server stava scegliendo gli indici errati a causa dei dati errati sul conteggio delle righe.

Stai eseguendo SQL 2005 e BOL afferma che nel 2005 non dovresti più dover eseguire UpdateUsage, ma poiché sei in modalità compatibilità 2000 potresti scoprire che è ancora necessario.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top