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 1
la query, ma senza alcun risultato - quel passo è andato, ma la scelta / prestazione scadente non lo è. Ho corso anche sp_updatestats
questa 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
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:
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:
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
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 :
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.enddate
controfrom #mac join t on t.TimeValue between sma.StartDate and sma.enddate join f on f.TimeID = t.TimeID and f.macid = sma.macid
TOP 50
dovrebbe comunque essere eseguito rapidamente. Puoi caricare i piani XML? Devo guardare i conteggi delle righe. Puoi eseguire il TOP 50
maxdop 1 e come selezione, non come aggiornamento e pubblicare il piano? (Cercando di semplificare / sezionare lo spazio di ricerca).
t.TimeValue between sma.StartDate and sma.enddate
potrebbe finire per generare molte più righe inutili che verranno successivamente filtrate nel join contro FactSubscriber e quindi non finiranno nel risultato finale.
sp_updatestatistics
sul tavolo?