Query T-SQL che utilizza un piano completamente diverso a seconda del numero di righe che sto aggiornando


20

Ho un'istruzione SQL UPDATE con una clausola "TOP (X)" e la riga in cui sto aggiornando i valori ha circa 4 miliardi di righe. Quando utilizzo "TOP (10)", ottengo un piano di esecuzione che viene eseguito quasi istantaneamente, ma quando utilizzo "TOP (50)" o più grande, la query non (almeno, non mentre aspetto) termina e utilizza un piano di esecuzione completamente diverso. La query più piccola utilizza un piano molto semplice con una coppia di ricerche di indice e un join di ciclo nidificato, in cui la stessa query esatta (con un numero diverso di righe nella clausola TOP dell'istruzione UPDATE) utilizza un piano che prevede due ricerche di indice diverse , una bobina da tavolo, parallelismo e un sacco di altre complessità.

Ho usato "OPTION (USE PLAN ...)" per costringerlo a utilizzare il piano di esecuzione generato dalla query più piccola - quando lo faccio, posso aggiornare fino a 100.000 righe in pochi secondi. So che il piano di query è buono, ma SQL Server sceglierà quel piano da solo solo quando è coinvolto solo un piccolo numero di righe - qualsiasi conteggio delle righe abbastanza grande nel mio aggiornamento comporterà il piano non ottimale.

Ho pensato che il parallelismo potesse essere la colpa, quindi ho impostato MAXDOP 1la query, ma senza alcun risultato - quel passo è andato, ma la scelta / prestazione scadente non lo è. Ho corso anche sp_updatestatsquesta mattina per assicurarmi che non fosse la causa.

Ho allegato i due piani di esecuzione: il più breve è anche il più veloce. Inoltre, ecco la query in questione (vale la pena notare che il SELECT che ho incluso sembra essere veloce nei casi di conteggi di righe sia piccole che grandi):

    update top (10000) FactSubscriberUsage3
               set AccountID = sma.CustomerID
    --select top 50 f.AccountID, sma.CustomerID
      from FactSubscriberUsage3 f
      join dimTime t
        on f.TimeID = t.TimeID
      join #mac sma
        on f.macid = sma.macid
       and t.TimeValue between sma.StartDate and sma.enddate 
     where f.AccountID = 0 --There's a filtered index on the table for this

Ecco il piano rapido : Piano di esecuzione rapida

Ed ecco quello più lento : Piano di esecuzione lento

C'è qualcosa di ovvio nel modo in cui sto impostando la mia query o nel piano di esecuzione purché si presti alla cattiva scelta che il motore di query sta facendo? Se necessario, posso anche includere le definizioni di tabella coinvolte e gli indici che sono definiti su di esse.

Per coloro che hanno chiesto una versione solo statistica degli oggetti del database: non avevo nemmeno capito che potevi farlo, ma ha perfettamente senso! Ho provato a generare gli script per un database di sole statistiche in modo che altri potessero testare da soli i piani di esecuzione, ma posso generare statistiche / istogrammi nel mio indice filtrato (errore di sintassi nello script, a quanto pare), quindi sono sfortunato lì. Ho provato a rimuovere il filtro e i piani di query erano vicini, ma non esattamente lo stesso, e non voglio mandare nessuno a caccia d'oca.

Aggiornamento e alcuni piani di esecuzione più completi: prima di tutto, Explorer Plan di SQL Sentry è uno strumento incredibile. Non sapevo nemmeno che esistesse fino a quando non ho visto le altre domande sul piano di query su questo sito, e aveva un bel po 'da dire su come le mie query stavano eseguendo. Sebbene non sia sicuro di come affrontare il problema, hanno reso evidente quale sia il problema.

Ecco il riepilogo per 10, 100 e 1000 righe: puoi vedere che la query di 1000 righe è molto lontana dagli altri: Riepilogo delle dichiarazioni

Puoi vedere che la terza query ha un numero ridicolo di letture, quindi ovviamente sta facendo qualcosa di completamente diverso. Ecco il piano di esecuzione stimato, con conteggi delle righe. Piano di esecuzione stimato di 1000 righe: Piano di esecuzione stimato di 1000 righe

Ed ecco i risultati effettivi del piano di esecuzione (a proposito, per "non finisce mai", si scopre che intendevo "finisce tra un'ora"). Piano di esecuzione effettivo di 1000 righe Piano di esecuzione effettivo di 1000 righe

La prima cosa che ho notato è che, invece di tirare 60K righe dalla tabella DimTime come si aspetta, in realtà tirando 1,6 miliardi, con un B . Guardando la mia domanda, non sono sicuro di come stia ritirando tante righe dalla tabella dimTime. L'operatore TRA che sto usando assicura solo che sto estraendo il record corretto da #mac in base al record del tempo nella tabella dei fatti. Tuttavia, quando aggiungo una riga alla clausola WHERE in cui filtro t.TimeValue (o t.TimeID) su un singolo valore, posso aggiornare con successo 100.000 righe in pochi secondi. Di conseguenza, e come chiarito nei piani di esecuzione che ho incluso, è ovvio che è il mio orario è il problema, ma non sono sicuro di come cambierei i criteri di join per aggirare questo problema e mantenere l'accuratezza . qualche idea?

Per riferimento, qui il piano (con numero di righe) per l'aggiornamento di 100 righe. Puoi vedere che colpisce lo stesso indice, e ancora con una tonnellata di righe, ma da nessuna parte vicino alla stessa entità di un problema. Esecuzione di 100 righe con numero di righe : inserisci qui la descrizione dell'immagine


Sono le statistiche di GOTTA Be. Hai eseguito un sp_updatestatisticssul tavolo?
JNK,

@JNK: inizialmente lo pensavo, ma ho già eseguito sp_updatestats senza modifiche. L'ho appena eseguito di nuovo e non mi importava aggiornare le statistiche su nessuno degli indici coinvolti nella query. Grazie comunque!
SqlRyan,

Il secondo è un piano di aggiornamento ampio (per indice) anziché uno stretto (per riga) che spiega parte della complessità extra visibile. Ma davvero l'unica differenza è unire l'ordine from #mac sma join f on f.macid = sma.macid join dimTime t on f.TimeID = t.TimeID and t.TimeValue between sma.StartDate and sma.enddatecontrofrom #mac join t on t.TimeValue between sma.StartDate and sma.enddate join f on f.TimeID = t.TimeID and f.macid = sma.macid
Martin Smith il

C'è qualcosa di sbagliato qui. Anche il costoso piano di query dovrebbe generare file in modo incrementale. A TOP 50dovrebbe comunque essere eseguito rapidamente. Puoi caricare i piani XML? Devo guardare i conteggi delle righe. Puoi eseguire il TOP 50maxdop 1 e come selezione, non come aggiornamento e pubblicare il piano? (Cercando di semplificare / sezionare lo spazio di ricerca).
usr

La partecipazione di @usr t.TimeValue between sma.StartDate and sma.enddatepotrebbe finire per generare molte più righe inutili che verranno successivamente filtrate nel join contro FactSubscriber e quindi non finiranno nel risultato finale.
Martin Smith,

Risposte:


3

L'indice su dimTime sta cambiando. Il piano più veloce utilizza un indice _dta. Prima di tutto, assicurati che non sia contrassegnato come un indice ipotetico in sys.indexes.

Pensando che potresti bypassare alcune parametrizzazioni utilizzando la tabella #mac per filtrare invece di fornire solo le date di inizio / fine come questa DOVE t.TimeValue tra @StartDate e @enddate. Sbarazzati di quel tavolo temporaneo.


L'indice con prefisso dta sembra proprio che sia stato creato seguendo una raccomandazione DTA senza personalizzare il nome. Gli indici ipotetici non possono apparire nei piani di esecuzione effettivi (e non verranno stimati neanche senza alcuni comandi non documentati). Non sono sicuro di come funzionerebbe il tuo secondo suggerimento. t.TimeValue between sma.StartDate and sma.enddateè correlato, quindi può cambiare per ogni riga della #temptabella. Con che cosa lo sostituirebbe l'OP?
Martin Smith,

Abbastanza giusto, non ho prestato abbastanza attenzione al tavolo temporaneo.
william_a_dba,

1
Tuttavia, gli indici ipotetici possono effettivamente rovinare un piano di esecuzione. Se è ipotetico, dovrebbe essere eliminato e ricreato. blogs.technet.com/b/anurag_sharma/archive/2008/04/15/…
william_a_dba

Gli indici ipotetici vengono lasciati quando il DTA non termina / si blocca prima del completamento. È necessario ripulirli manualmente in caso di singhiozzo con il DTA.
william_a_dba,

1
@william_a_dba - Ah capisco cosa intendi ora (dopo aver letto il tuo link). La query non finisce mai potrebbe essere stata continuamente ricompilata. Interessante!
Martin Smith,

1

Senza ulteriori informazioni sui conteggi delle righe nel piano, la mia raccomandazione preliminare è di organizzare l'ordine di join corretto nella query e forzarlo utilizzando OPTION (FORCE ORDER). Applicare l'ordine di join del primo piano.

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.