Le funzioni della finestra causano un piano di esecuzione terribile quando vengono chiamate da una vista con clausola 'where' parametrizzata esterna


10

Ho avuto questo problema molto tempo fa, ho trovato una soluzione alternativa che mi andava bene e me ne sono dimenticato.

Ma ora c'è questa domanda su SO, quindi sono disposto a sollevare questo problema.

C'è una vista che unisce alcune tabelle in modo molto semplice (ordini + righe ordine).

Quando viene eseguita una query senza una whereclausola, la vista restituisce diversi milioni di righe.
Tuttavia, nessuno lo chiama mai così. La solita domanda è

select * from that_nasty_view where order_number = 123456;

Questo restituisce circa 10 record su 5m.

Una cosa importante: la vista contiene una funzione di finestra rank(), che è partizionata esattamente dal campo in cui viene sempre interrogata la vista:

rank() over (partition by order_number order by detail_line_number)

Ora, se questa vista viene interrogata con parametri letterali nella stringa di query, esattamente come mostrato sopra, restituisce immediatamente le righe. Il piano di esecuzione va bene:

  • Ricerca indice su entrambe le tabelle utilizzando gli indici su order_number(restituisce 10 righe).
  • Calcolo delle finestre sul risultato minuscolo restituito.
  • Selezione.

Tuttavia, quando la vista viene chiamata in modo parametrizzato, le cose diventano cattive:

  • Index scansu tutte le tabelle ignorando gli indici. Restituisce 5m righe.
  • Enorme join.
  • Calcolo di tutte le finestre partition(circa 500.000 finestre).
  • Filter per prendere 10 file su 5m.
  • Selezionare

Ciò accade in tutti i casi in cui sono coinvolti parametri. Può essere SSMS:

declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;

Può essere un client ODBC, come Excel:

select * from that_nasty_view where order_number = ?

Oppure può essere qualsiasi altro client che utilizza parametri e non concatenazione sql.

Se la funzione finestra viene rimossa dalla vista, viene eseguita perfettamente rapidamente, indipendentemente dal fatto che venga eseguita una query con i parametri.

La mia soluzione era rimuovere la funzione offensiva e riapplicarla in un secondo momento.

Ma cosa dà? È davvero un bug nel modo in cui SQL Server 2008 gestisce le funzioni della finestra?


order_number è la chiave primaria? I tipi di dati di colonna e parametro corrispondono?
gbn

order_numbernon è una chiave primaria. È int not nullcon indice non cluster su di esso in entrambe le tabelle.
GSerg

5
SQL Server 2005 ha riscontrato problemi con il push dei predicati in quest'area. Pensavo fossero risolti ora. A proposito, il tuo esempio TSQL utilizza una variabile non un parametro. L'aggiunta di OPTION (RECOMPILE)aiuto?
Martin Smith,

1
@GSerg - Quindi sul piano negativo con l'ultimo filtro ha circa 5 milioni di righe nel filtro e circa 10 righe in uscita corrispondenti all'attuale? Se è così, forse è quel problema di push del predicato che non è stato ancora completamente risolto.
Martin Smith,

Risposte:


5

Questo sembra essere un problema di vecchia data che continua a riemergere in un modo o nell'altro ed è ancora presente in SQL Server 2012.

Alcuni post ne discutono

Tutte le versioni correnti di SQL Server fino al 2012 compreso non sono in grado di inviare il filtro su un gruppo di partizionamento oltre il progetto di sequenza per un predicato con parametri, tranne se option(recompile)utilizzato (se 2008+).

Un'alternativa al recompilesuggerimento sarebbe quella di riscrivere la query per usare un TVF in linea parametrizzato come suggerito da @ a1ex07)


Ho avuto il caso anche in SQL Server 2014
Guillaume86,

3

Proverei a sostituire la vista con udf con valori di tabella. In questo modo filtrerà prima i record e quindi applicherà la funzione finestra. Questa funzione può accettare i parametri della tabella in modo da poterne passare più order_numbervolte


Ancora un'altra soluzione, sì. Non potevo farlo, però, perché non tutti i clienti erano in grado di utilizzare una funzione con valori di tabella.
GSerg

Perché? Non sono sicuro al 100%, ma penso che tutto ciò di cui hai bisogno sia cambiare un po 'la query in qualcosa del genereSELECT * FROM my_funct(12345)
a1ex07

Uno dei requisiti era che la query è utilizzabile dagli utenti finali utilizzando Excel (ovvero da MS Query) e MS Query non ti consentirà di farlo, almeno nelle versioni fino al 2003.
GSerg

it will filter records first, and then apply window functionnon è corretto. Non esiste un ordine deterministico per l'esecuzione
Remus Rusanu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.