2012-10-19 11 views
7

Date un'occhiata a questo violino: http://sqlfiddle.com/#!6/18324/2SQL Server utilizza la scansione invece di cercare quando si utilizza una funzione di finestra e predicato contiene una variabile

Espandere il primo piano di esecuzione, per le query vista B.
Si noti che la prima query viene eseguita utilizzando index seek, mentre la seconda esegue l'index scan. Nella mia configurazione reale, con migliaia di righe, questo produce un impatto sulle prestazioni che è piuttosto considerevole.

WTF ???

Le query sono equivalenti, vero? Perché una ricerca letterale cerca e una scansione variabile?
Ma ancora più importante: come posso aggirare questo?

This post arriva più vicino al problema e la soluzione che funziona da lì utilizza option(recompile) (grazie, Martin Smith). Tuttavia, questo non funziona per me, perché le mie query vengono generate dalla mia libreria ORM (che è Entity Framework) e non posso modificarle manualmente.
Piuttosto, quello che sto cercando è un modo per riformulare la vista B in modo che il problema non si verifichino.

Mentre si scherza con questo problema, ho notato che è sempre il blocco "Segmento" nel piano di esecuzione che perde il predicato. Per verificare ciò, ho riformulato la query in termini di una sottoquery con la funzione min (vedere la vista D). E voilà! - entrambe le query sulla vista D producono piani identici.

La cattiva notizia, tuttavia, è che non posso usare questa min trucco -powered, perché nella mia vera messa a punto, la colonna Y è in realtà diverse colonne, in modo che posso ordinare da loro, ma non riesco a prendere un min() di loro.
Quindi la seconda domanda sarebbe: qualcuno può inventare un trucco simile alla subquery con alimentazione minima, ma funziona per più colonne?

NOTA 1: questo non è assolutamente correlato al punto di non ritorno, perché ci sono solo 2 record nella tabella.
NOTA 2: non ha nemmeno a che fare con la presenza di una vista. Guarda un esempio con la vista C: il server sta usando felicemente la ricerca in quel caso.

+2

Sulla mia istanza R2 2008 locale 'selezionare * da B dove X = @a opzione (ricompilare)' produce una ricerca. –

+0

@ Martin Smith: Giusto. Mi è mancato. Grazie. Sfortunatamente, tuttavia, questo non funziona. Vedi il mio aggiornamento alla domanda. –

+0

Molto probabilmente un duplicato: http://dba.stackexchange.com/questions/12498/window-functions-cause-awful-execution-plan-when-called-from-a-view-with-externa – GSerg

risposta

0

Ecco la mia risposta.

In definitiva, ho usato il trucco min -powered, e ho avuto intorno al fatto che Y è in realtà diverse colonne convertendo quelle colonne in rappresentazioni di stringa costante di lunghezza (messi a punto per l'ordinamento) e unendo le stringhe in un'unica stringa . Fatto ciò, sono in grado di utilizzare quella stringa unita come argomento per min().

Vorrei ancora sapere il modo corretto di farlo. Se qualcuno capisce di saperlo, lo apprezzerei.

0

Può essere questo uno funzionerà

select a.X, a.Y from A a 
    cross apply 
     (select top 1 * from A t where t.X = a.X order by t.Y asc) as idx 

SQLFiddle http://sqlfiddle.com/#!6/a3362/2

+0

Provato quello. È vero, produce una ricerca. Ma ne produce due, e poi un anello annidato si unisce tra loro due. –

+0

hai provato quello in ambiente reale? Dovrebbe essere quasi veloce come il minigioco –

+0

No, non l'ho provato in un ambiente reale, ma non vedo come dovrebbe essere quasi altrettanto veloce. Sta facendo due ricerche invece di uno più un extra join, quindi dovrebbe essere almeno due volte più complesso, non è vero? –

0

Le query producono uscita equivalente, ma in the eyes of the sql optimizer they are different. L'articolo consiglia di guardare il OPTION clause (unfortunately not included before SQL 2005).

È possibile Brew Your Own Query on top of the Entity Framework, che può essere la soluzione migliore per ottenere la prestazione desiderata.

+2

Quando il server SQL deve confrontare due valori di tipi diversi, utilizza le regole per sapere come eseguire il cast. http://msdn.microsoft.com/en-us/library/ms190309.aspx. Quando il server SQL confronta varchar e uniqueidentifier, lancia varchar a uniqueidentifier, quindi in pratica quando scrivi 'X = '40DB7DE2-EEFA-4D31-B400-7E72AB34DE99'' e X è uniqueidentifier, significa' X = cast (' 40DB7DE2- EEFA-4D31-B400-7E72AB34DE99 'come identificatore univoco) ' Ecco SQLFiddle con due query, una con varchar e una con identificatore univoco, i piani sono uguali http://sqlfiddle.com/#!6/18324/19 –

+2

@RyanGates : In primo luogo, l'idea che "c'è una conversione per ogni record" semplicemente non è vera. In secondo luogo, è in realtà la prima query (quella con valore letterale) che esegue il modo in cui voglio che funzioni, mentre la seconda query (con variabile) mostra prestazioni peggiori. –

Problemi correlati