Warum kann meine Indexsuche die richtige Anzahl von Zeilen schätzen und der Sortieroperator nicht?

11

Ich habe eine Abfrage, die eine Funktion für das Prädikat verwendet, ungefähr so:

commentType = 'EL'
AND commentDateTime >= DATEADD(month,datediff(month,0,getdate()) - 13,0)

Ich habe einen gefilterten Index für commentType mit 40 KB Zeilen. Wenn ich die Abfrage ausführe, ist die geschätzte Anzahl der Zeilen für die Indexsuche sehr genau (ca. 11 KB), aber für den nächsten Schritt (Sortieroperator) werden die Statistiken und vollständig ignoriert schätzt nur die Gesamtzahl der Zeilen im gefilterten Index.

Warum passiert dies? Ich kenne die Grundlagen der Sargabilität und habe sie nur aus Gründen der Vernunft getestet, indem ich das Datum durch ein aktuelles Datum (01.01.2014) und voila ersetzt habe ... Die Sortierung hat begonnen, die Anzahl der Zeilen richtig zu erraten ...

Warum passiert das und wie kann ich es beheben? Ich kann keinen festen Termin überschreiten ...

MrKudz
quelle
DATEADD(month,datediff(month,0,getdate()) - 13,0)macht für mich keinen Sinn. Was versuchst du damit zu machen? Darf es verbessert / vereinfacht werden?
Daniel Hutmacher
2
@ Daniel Das ist der Anfang des Monats, vor 13 Monaten.
Aaron Bertrand
1
Bearbeiten Sie Ihre Frage außerdem entsprechend der Version von SQL Server (?), Auf der Sie sich befinden. Verwenden Sie dafür Tags.
Daniel Hutmacher
Könnten Sie versuchen, DATEADD(month, -13, DATEADD(day, 1-DATEPART(day, SYSDATETIME()))festzustellen, ob es einen Unterschied gibt?
Daniel Hutmacher
Wenn Sie einen nicht gefilterten Index haben (commentType, commentDate), verhält er sich dort besser? Es ist nur so, dass gefilterte Indizes manchmal Schätzungen an verschiedenen Stellen in Plänen falsch melden können. Die Schätzung scheint ein Ausweg zu sein, wenn die Gesamtzahl im gefilterten Index angegeben wird, aber tatsächlich wird der Plan falsch angezeigt.
Rob Farley

Antworten:

9

Ich glaube, Ihre Schätzungen sind falsch, weil ein Schätzerfehler zwei der DATEDIFF-Argumente vertauscht. Ich spreche hier darüber:

Eine Problemumgehung besteht darin, den ersten Tag vor 13 Monaten ohne Verwendung von DATEDIFF (2008+) zu berechnen:

DATEADD(MONTH, -13, DATEADD(DAY, 1-DATEPART(DAY,GETDATE()), CONVERT(DATE, GETDATE()));

Ich bin nicht positiv , dass die Schätzung Adresse wird (ich habe nicht mit gefilterten Indizes getestet, und ich bin nicht sicher , was die Art tatsächlich tut oder warum es hat eine andere Schätzung ohne Plan und / oder den Rest der Abfrage ).

Das von Microsoft empfohlene Update ist die Verwendung von TF 4199, aber ich bin mir nicht sicher, ob Sie dies hier tun müssen:

Eine andere Möglichkeit wäre, sicherzustellen, dass Sie für jede Version von SQL Server, die Sie verwenden, auf dem absolut neuesten SP / CU sind, da dies im folgenden KB-Artikel behoben ist (obwohl hierfür weiterhin TF 4199 erforderlich ist es sei denn, Sie sind auf 2014 oder besser):

Das Update kann mit den folgenden Builds erhalten werden:

  • 2005 SP3 CU 15 (> = 9.00.4325 UND <= 9.00.4999)
  • 2005 SP4 CU 2 (> = 9.00.5259)
  • 2008 SP1 CU 13 (> = 10.00.2816.00 UND <= 10.00.3999)
  • 2008 SP2 CU 3 (> = 10.00.4279.00 UND <= 10.00.5499)
  • Durch Erweiterung 2008 SP3 & SP4 (> = 10.00.5500)
  • 2008 R2 CU 7 (10.50.1777.0)
  • 2008 R2 SP1 CU 3 (> = 10.50.2769.0 UND <= 10.50.3999)
  • Durch Erweiterung 2008 R2 SP2 & SP3 (> = 10.50.4000)
  • Durch Erweiterung 2012, 2014, 2016 (> = 11,0)

(Bitte geben Sie beim nächsten Mal die Ergebnisse von SELECT @@VERSIONin Ihre Frage ein.)

Ich werde bemerken, dass der KB-Artikel besagt, dass DATEDIFF die Anzahl der Zeilen unterschätzen kann , was das Gegenteil von dem ist, was in Ihrem Szenario passiert. Das bedeutet nicht, dass die Korrekturen nicht auf Sie zutreffen. Ich denke, der Wortlaut des KB-Artikels ist ungenau, da die Schätzungen je nach den Daten und dem Bereich, den Sie betrachten, in beide Richtungen gehen können.

Mein Blog-Beitrag oben hat bestätigt, dass der Austausch ab 2014 nicht mehr stattfindet. Um sicher zu gehen, würde ich wahrscheinlich nur DATEDIFF aus Ihrem Prädikat streichen und eine andere Methode verwenden, um den Beginn Ihres Bereichs zu berechnen. Ich empfehle nicht den Overkill von 4199 oder die Verwendung von dynamischem SQL, um den fehlerhaften Austausch zu verhindern.

Aaron Bertrand
quelle
Danke für die Hilfe ! Ich habe Ihren Vorschlag ausprobiert und der Plan geändert. So war es vorher: s16.postimg.org/t5j6o1yed/fix_wrong.png So ist es, nachdem ich mein Dateiff von dir geändert habe: postimg.org/image/5f725rj83 Ich werde alle URLs lesen, die du mir gegeben hast . Prost.
MrKudz