Ich habe eine SQL UPDATE-Anweisung mit einer "TOP (X)" - Klausel, und die Zeile, in der ich Werte aktualisiere, enthält ungefähr 4 Milliarden Zeilen. Wenn ich "TOP (10)" verwende, erhalte ich einen Ausführungsplan, der fast sofort ausgeführt wird. Wenn ich jedoch "TOP (50)" oder höher verwende, wird die Abfrage nie (zumindest nicht, solange ich warte) beendet Es wird ein völlig anderer Ausführungsplan verwendet. Die kleinere Abfrage verwendet einen sehr einfachen Plan mit einem Paar von Indexsuchen und einem Join mit verschachtelten Schleifen, wobei genau dieselbe Abfrage (mit einer anderen Anzahl von Zeilen in der TOP-Klausel der UPDATE-Anweisung) einen Plan verwendet, der zwei verschiedene Indexsuchen umfasst , eine Tabellenspule, Parallelität und eine Menge anderer Komplexität.
Ich habe "OPTION (USE PLAN ...)" verwendet, um zu erzwingen, dass der von der kleineren Abfrage generierte Ausführungsplan verwendet wird. Wenn ich dies tue, kann ich in wenigen Sekunden bis zu 100.000 Zeilen aktualisieren. Ich weiß, dass der Abfrageplan gut ist, aber SQL Server wählt diesen Plan nur dann alleine aus, wenn nur eine geringe Anzahl von Zeilen betroffen ist. Jede anständig große Zeilenanzahl in meinem Update führt zu einem suboptimalen Plan.
Ich dachte, die Parallelität könnte schuld sein, also stellte ich MAXDOP 1
die Abfrage ein, aber ohne Wirkung - dieser Schritt ist weg, aber die schlechte Wahl / Leistung ist es nicht. Ich bin auch sp_updatestats
erst heute Morgen gelaufen, um sicherzugehen, dass das nicht die Ursache ist.
Ich habe die beiden Ausführungspläne angehängt - der kürzere ist auch der schnellere. Außerdem ist hier die betreffende Abfrage (es ist erwähnenswert, dass das von mir eingeschlossene SELECT sowohl bei kleinen als auch bei großen Zeilenzahlen schnell zu sein scheint):
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
Gibt es irgendetwas Offensichtliches in der Art und Weise, wie ich meine Abfrage einrichte, oder im Ausführungsplan, sofern es sich für die schlechte Wahl eignet, die die Abfrage-Engine trifft? Bei Bedarf kann ich auch die beteiligten Tabellendefinitionen und die darauf definierten Indizes einfügen.
An diejenigen, die nach einer Nur-Statistiken-Version der Datenbankobjekte fragten: Ich wusste nicht einmal, dass Sie das tun können, aber es ist absolut sinnvoll! Ich habe versucht, die Skripte für eine reine Statistikdatenbank zu generieren, damit andere die Ausführungspläne selbst testen können, aber ich kann auf meinem gefilterten Index Statistiken / Histogramme generieren (Syntaxfehler im Skript, so scheint es) aus Glück dort. Ich habe versucht, den Filter zu entfernen, und die Abfragepläne waren eng, aber nicht genau gleich, und ich möchte niemanden auf eine Gänsehaut schicken.
Update und einige vollständigere Ausführungspläne: Zunächst einmal ist der Plan Explorer von SQL Sentry ein unglaubliches Tool. Ich wusste nicht einmal, dass es sie gibt, bis ich die anderen Fragen zum Abfrageplan auf dieser Site gelesen habe, und es gab einiges darüber zu sagen, wie meine Abfragen ausgeführt wurden. Obwohl ich nicht sicher bin, wie ich das Problem angehen soll, haben sie deutlich gemacht, woran es liegt.
Hier ist die Zusammenfassung für 10, 100 und 1000 Zeilen - Sie können sehen, dass die 1000-Zeilen-Abfrage weit davon entfernt ist, mit den anderen übereinzustimmen:
Sie können sehen, dass die dritte Abfrage eine lächerliche Anzahl von Lesevorgängen aufweist, sodass sie offensichtlich etwas völlig anderes ausführt. Hier ist der geschätzte Ausführungsplan mit Zeilenzahlen. Geschätzter Ausführungsplan für 1000 Zeilen:
Und hier sind die tatsächlichen Ergebnisse des Ausführungsplans (übrigens mit "nie beendet", es stellte sich heraus, dass ich "in einer Stunde beendet" meinte). Tatsächlicher Ausführungsplan mit 1000 Zeilen
Das erste, was mir aufgefallen ist, ist, dass, anstatt wie erwartet 60.000 Zeilen aus der dimTime-Tabelle zu ziehen, tatsächlich 1,6 Milliarden mit einem B gezogen werden . Wenn ich meine Abfrage betrachte, bin ich mir nicht sicher, wie viele Zeilen aus der dimTime-Tabelle zurückgezogen werden. Der BETWEEN-Operator, den ich verwende, stellt lediglich sicher, dass ich den richtigen Datensatz aus #mac basierend auf dem Zeitdatensatz in der Faktentabelle ziehe. Wenn ich jedoch der WHERE-Klausel eine Zeile hinzufüge, in der ich t.TimeValue (oder t.TimeID) nach einem einzelnen Wert filtere, kann ich innerhalb von Sekunden 100.000 Zeilen erfolgreich aktualisieren. Infolgedessen und wie in den Ausführungsplänen, die ich beigefügt habe, verdeutlicht, ist es offensichtlich, dass mein Zeitplan das Problem ist, aber ich bin nicht sicher, wie ich die Join-Kriterien ändern würde, um dieses Problem zu umgehen und die Genauigkeit zu gewährleisten . Irgendwelche Gedanken?
Als Referenz hier der Plan (mit Zeilenzahlen) für die Aktualisierung von 100 Zeilen. Sie können sehen, dass es auf den gleichen Index trifft und immer noch eine Tonne Zeilen enthält, aber bei weitem nicht das gleiche Ausmaß eines Problems aufweist. 100 Zeilen Ausführung mit Zeilenanzahl :
sp_updatestatistics
auf den Tisch laufen lassen ?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
gegenfrom #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
sollte immer noch schnell ausgeführt werden. Können Sie die XML-Pläne hochladen? Ich muss mir die Zeilenzahlen ansehen. Kannst du dasTOP 50
mit maxdop 1 und als select, nicht als update ausführen und den plan posten? (Versuch, den Suchraum zu vereinfachen / zu halbieren).t.TimeValue between sma.StartDate and sma.enddate
dazu führen, dass viel mehr nutzlose Zeilen generiert werden, die später im Join gegen FactSubscriber herausgefiltert werden und daher nicht im Endergebnis enden.Antworten:
Der Index für dimTime ändert sich. Der schnellere Plan verwendet einen _dta-Index. Stellen Sie zunächst sicher, dass in sys.indexes kein hypothetischer Index angegeben ist.
Denken Sie, Sie könnten einige Parametrisierungen umgehen, indem Sie die #mac-Tabelle zum Filtern verwenden, anstatt nur das Start- / Enddatum wie dieses anzugeben. WHERE t.TimeValue zwischen @StartDate und @enddate. Werde die temporäre Tabelle los.
quelle
t.TimeValue between sma.StartDate and sma.enddate
ist korreliert, kann sich also für jede Zeile in der#temp
Tabelle ändern . Womit würde das OP es ersetzen?Ohne weitere Informationen zu den Zeilenzahlen im Plan ist es meine vorläufige Empfehlung, die richtige Verknüpfungsreihenfolge in der Abfrage zu arrangieren und sie mit zu erzwingen
OPTION (FORCE ORDER)
. Erzwingen Sie die Verknüpfungsreihenfolge des ersten Plans.quelle