Ich habe eine Abfrage, bei der mit select *
nicht nur viel weniger Lesevorgänge, sondern auch deutlich weniger CPU-Zeit als mit select c.Foo
.
Dies ist die Abfrage:
select top 1000 c.ID
from ATable a
join BTable b on b.OrderKey = a.OrderKey and b.ClientId = a.ClientId
join CTable c on c.OrderId = b.OrderId and c.ShipKey = a.ShipKey
where (a.NextAnalysisDate is null or a.NextAnalysisDate < @dateCutOff)
and b.IsVoided = 0
and c.ComplianceStatus in (3, 5)
and c.ShipmentStatus in (1, 5, 6)
order by a.LastAnalyzedDate
Dies endete mit 2.473.658 logischen Lesevorgängen, hauptsächlich in Tabelle B. Es verwendete 26.562 CPU und hatte eine Dauer von 7.965.
Dies ist der generierte Abfrageplan:
Auf PasteThePlan: https://www.brentozar.com/pastetheplan/?id=BJAp2mQIQ
Wenn ich zu ändere c.ID
, *
wurde die Abfrage mit 107.049 logischen Lesevorgängen abgeschlossen, die ziemlich gleichmäßig auf alle drei Tabellen verteilt waren. Es verwendete 4.266 CPU und hatte eine Laufzeit von 1.147.
Dies ist der generierte Abfrageplan:
Auf PasteThePlan: https://www.brentozar.com/pastetheplan/?id=SyZYn7QUQ
Ich habe versucht, die von Joe Obbish vorgeschlagenen Abfragehinweise mit den folgenden Ergebnissen zu verwenden:
select c.ID
ohne Hinweis: https://www.brentozar.com/pastetheplan/?id=SJfBdOELm
select c.ID
mit Hinweis: https://www.brentozar.com/pastetheplan/ ? id = B1W ___ N87
select *
ohne Hinweis: https://www.brentozar.com/pastetheplan/?id=HJ6qddEIm
select *
mit Hinweis: https://www.brentozar.com/pastetheplan/?id=rJhhudNIQ
Durch die Verwendung des OPTION(LOOP JOIN)
Hinweises mit wurde select c.ID
die Anzahl der Lesevorgänge im Vergleich zur Version ohne Hinweis drastisch reduziert, es wird jedoch immer noch etwa die vierfache Anzahl der Lesevorgänge für die select *
Abfrage ohne Hinweise ausgeführt. Das Hinzufügen OPTION(RECOMPILE, HASH JOIN)
zu der select *
Abfrage hat die Leistung wesentlich verschlechtert als alles, was ich bisher versucht habe.
Nach dem Aktualisieren der Statistiken für die Tabellen und ihre Indizes mithilfe von WITH FULLSCAN
wird die select c.ID
Abfrage viel schneller ausgeführt:
select c.ID
Vor dem Update: https://www.brentozar.com/pastetheplan/?id=SkiYoOEUm
select *
Vor dem Update: https://www.brentozar.com/ pastetheplan /? id = ryrvodEUX
select c.ID
nach dem Update: https://www.brentozar.com/pastetheplan/?id=B1MRoO487
select *
nach dem Update: https://www.brentozar.com/pastetheplan/?id=Hk7si_V8m
select *
Immer noch besser als die select c.ID
Gesamtdauer und die Gesamtanzahl der Lesevorgänge ( select *
hat etwa die Hälfte der Lesevorgänge), verbraucht jedoch mehr CPU. Insgesamt sind sie viel näher als vor dem Update, jedoch unterscheiden sich die Pläne immer noch.
Das gleiche Verhalten ist für 2016 im Kompatibilitätsmodus 2014 und für 2014 zu beobachten. Was könnte die Diskrepanz zwischen den beiden Plänen erklären? Könnte es sein, dass die "richtigen" Indizes nicht erstellt wurden? Könnten leicht veraltete Statistiken dazu führen?
Ich habe versucht, ON
die Vergleichselemente auf mehrere Arten in den Teil des Joins zu verschieben, aber der Abfrageplan ist jedes Mal derselbe.
Nach Index-Neuerstellungen
Ich habe alle Indizes für die drei an der Abfrage beteiligten Tabellen neu erstellt. c.ID
führt immer noch die meisten Lesevorgänge aus (mehr als doppelt so viele *
), die CPU-Auslastung beträgt jedoch etwa die Hälfte der *
Version. Die c.ID
Version auch verschüttete in tempdb auf die Sortierung von ATable
:
c.ID
: https://www.brentozar.com/pastetheplan/?id=HyHIeDO87
*
: https://www.brentozar.com/pastetheplan/?id=rJ4deDOIQ
Ich habe auch versucht, den Betrieb ohne Parallelität zu erzwingen. Dies ergab die beste Abfrage: https://www.brentozar.com/pastetheplan/?id=SJn9-vuLX
Ich bemerke die Ausführungsanzahl der Operatoren NACH dem großen Index-Suchvorgang, bei dem die Bestellung in der Singlethread-Version nur 1.000 Mal ausgeführt wird, in der parallelisierten Version jedoch deutlich mehr, und zwar zwischen 2.622 und 4.315 Ausführungen verschiedener Operatoren.
select c.ID
Abfrage wurde dadurch viel schneller, es wird jedoch noch zusätzliche Arbeit geleistet, die dieselect *
Abfrage ohne Hinweise leistet.Veraltete Statistiken können sicher dazu führen, dass der Optimierer eine schlechte Methode zum Auffinden der Daten auswählt. Haben Sie versucht, einen Index zu erstellen
UPDATE STATISTICS ... WITH FULLSCAN
oder einen vollständigenREBUILD
Index zu erstellen? Versuchen Sie das und sehen Sie, ob es hilft.AKTUALISIEREN
Nach einem Update aus dem OP:
Wenn die einzige Maßnahme nun darin bestand
UPDATE STATISTICS
, einen Index zu erstellenREBUILD
(nichtREORGANIZE
), da ich gesehen habe, dass dies bei geschätzten Zeilenzahlen hilft, bei denen sowohl derUPDATE STATISTICS
Index alsREORGANIZE
auch der Index nicht erfolgreich waren.quelle
a) Schreiben Sie jede Tabelle als Unterabfrage neu, und beachten Sie dabei die folgenden Regeln:
b) SELECT - Join-Spalten zuerst setzen
c) PREDICATES - In die entsprechenden Unterabfragen verschieben
d) ORDER BY - In die entsprechenden Unterabfragen verschieben entsprechende Unterabfragen sortieren nach JOIN COLUMNS FIRST
e) Fügen Sie eine Wrapper-Abfrage für Ihre endgültige Sortierung und SELECT hinzu.
Die Idee ist, Verknüpfungsspalten in jeder Unterauswahl vorzuordnen, wobei Verknüpfungsspalten in jeder Auswahlliste an erster Stelle stehen.
Hier ist was ich meine ...
quelle
ORDER BY
in einer Unterabfrage ohne TOP, FORXML usw. ist es ungültig. Ich habe es ohne dieORDER BY
Klauseln versucht , aber es war der gleiche Plan.