2009-02-04 11 views
20

In un database di SQL Server 2005 Sto lavorando a questa query:Perchè una query con parametri produce notevolmente più lento piano di query vs di query senza parametri

select *
da foo
unirsi bar in bar .x = foo.x
unirsi Baz su baz.y = foo.y
dove foo.x = 1000

ha un piano di query molto diverso e più veloce rispetto alla versione seguente parametrizzato.

dichiarare @ p0 int
set @ p0 = 1000
select *
da foo
unirsi bar bar.x = foo.x
unirsi Baz su baz.y = foo.y
dove foo.x = @ p0

Nel mio caso particolare, la versione con letterale viene eseguita in un secondo tempo inferiore. La versione parametrizzata richiede 2-3 secondi. Mi aspettavo che fossero identici dato che sono la stessa domanda.

Perché ricevono piani di query diversi?

Esiste un modo per rendere la versione parametrizzata con le stesse prestazioni della versione letterale?

Ecco i piani di query. La mia vera query è piuttosto diversa da quella generica che ho fornito sopra, tuttavia l'UNICA differenza tra le due query che hanno prodotto questi piani è il parametro. Perché sostituire un letterale con un parametro risulta in piani così diversi?

+0

Hai controllato quali sono effettivamente i piani di esecuzione? (Utilizzando 'include il piano di esecuzione effettivo' in Management Studio) – Blorgbeard

+0

Sì, e sono completamente diversi. –

+0

Vuoi includere i piani di esecuzione? Possono indicare qual è il problema. –

risposta

8

Sembra che il pianificatore di query abbia preso una decisione nella query letterale basata su informazioni che già possiede. Avrebbe statistiche che può interrogare in modo efficiente in base alla diffusione dei dati forniti nel tuo letterale specifico.

La query con parametri ha scelto la query che ritiene più equa per tutti i dati nella tabella, che noterete sono molti cicli annidati (prestazioni = cattiva).

Forse potresti provare a eseguire gli strumenti di ottimizzazione del database sul tuo database per vedere se alcuni indici potrebbero aiutarti?

In particolare nella query, provate questo:

declare @p0 int 
set @p0 = 1000 
select * 
from foo 
join bar on bar.x = foo.x 
join baz on baz.y = foo.y 
where foo.x = @p0 
OPTION (OPTIMIZE FOR (@p0 = 1000)) 

Ma sarei diffidare di fare questo senza avere la certezza che i dati contenuti in questa query non cambierà e che la query su questo piano sarà sempre essere più efficiente

+0

Questo sembra essere quello che sta succedendo. L'hint OPTIMIZE ha reso la query con parametri lo stesso piano del valore letterale. –

+1

Questo non è corretto. L'ottimizzatore di query non considera equità. Utilizza solo i valori dei parametri al momento della compilazione, ma non rappresentativi. "OTTIMIZZA PER SCONOSCIUTO" avrebbe questo effetto. –

+0

Mi permetto di dissentire Martin. SQL Server memorizza le "statistiche" rispetto ai dati nella tabella (SD, spread, top 10 ecc.) Questi dati vengono valutati dal pianificatore di query per determinare il piano più efficiente per interrogare i dati. Vedere: http://technet.microsoft.com/en-us/library/cc966419.aspx. Il suggerimento qui sta costringendo il piano nel piano "ottimale" a causa dello specifico valore letterale 1000, piuttosto che del valore "sconosciuto". – Spence

2

Il punto di partenza dovrebbe essere il profiler SQL. Esegui entrambi attraverso il profiler e vedi qual è il piano di query in ogni caso ... quindi aggiorna la domanda per descrivere i due piani.

Una cosa che credo può essere un problema è che se si dispone di una query parametrizzata con un insieme di valori, l'ottimizzatore può guardare alcune delle statistiche/indici e scegliere un modo di farlo, poi il riutilizzo quel piano per tutte le domande - nonostante non sia particolarmente appropriato per un diverso insieme di valori. Allo stesso modo, se il piano viene determinato quando esiste un insieme di dati (ad esempio quando una tabella è piccola, incoraggiando una scansione della tabella) e quindi si aggiungono carichi di dati, il piano potrebbe non essere appropriato. Tuttavia, nessuno di questi elementi influirebbe su una query che era male come la prima query per l'istruzione preparata.

+0

Ho incluso schermate dei piani. –

6

Penso che stai eseguendo il comando di "parameter sniffing". In sostanza, ciò significa che SQL Server tenta di utilizzare tutte le informazioni necessarie per calcolare un piano di esecuzione ottimale per la query. Nel caso della prima query, si ha un valore costante noto al momento della compilazione, in modo che il motore possa ottimizzare direttamente il piano di query per quel valore.

Nella seconda, il fatto che si sta utilizzando una variabile maschera quel valore dal motore in fase di compilazione (si potrebbe pensare che dovrebbe essere in grado di capirlo, ma in realtà ho avuto problemi simili con un semplice espressione costante!), che porta a prestazioni scadenti.

Un modo per cercare di aggirare il problema sarebbe quello di avvolgere la query in una stored procedure che porta direttamente il parametro e poi lo applica alla query - qualcosa di simile:

CREATE PROCEDURE test 
    @p0 int 
AS 
BEGIN 
    select * 
    from foo 
    join bar on bar.x = foo.x 
    join baz on baz.y = foo.y 
    where foo.x = @p0 
END 

Questo dovrebbe consentire all'ottimizzatore di "annusare" il parametro che si utilizza e generare un piano di query ottimale per te.

+0

Questo mi ha salvato 2 minuti in un rapporto che richiedeva 2,30 minuti. Eccezionale! –

2

Nel mio caso nella colonna DB il tipo di colonna è stato definito come VarChar e nel parametro parametrico il tipo di parametro è stato definito come NVarChar, questo ha introdotto CONVERT_IMPLICIT nel piano di esecuzione effettivo per corrispondere al tipo di dati prima del confronto e che era colpevole per le prestazioni delle scrofe, 2 sec vs 11 sec. Correggere la query parametrizzata di tipo parametro effettuata con la stessa velocità della versione non parametrizzata.

Spero che questo possa aiutare qualcuno con problemi simili.

+0

Questo era esattamente il mio problema. Il driver db stava convertendo la mia stringa in una stringa unicode. – dgraves