Abfragepläne mit Bitmap-Filtern sind manchmal schwierig zu lesen. Aus dem BOL-Artikel für Repartitionsströme (Hervorhebung meiner):
Der Operator "Repartition Streams" verwendet mehrere Streams und erstellt mehrere Streams von Datensätzen. Der Inhalt und das Format des Datensatzes werden nicht geändert. Wenn der Abfrageoptimierer einen Bitmapfilter verwendet, wird die Anzahl der Zeilen im Ausgabestream verringert.
Außerdem ist ein Artikel zu Bitmap-Filtern hilfreich:
Bei der Analyse eines Ausführungsplans mit Bitmap-Filterung ist es wichtig zu verstehen, wie die Daten durch den Plan fließen und wo die Filterung angewendet wird. Der Bitmap-Filter und die optimierte Bitmap werden auf der Seite der Build-Eingabe (der Dimensionstabelle) eines Hash-Joins erstellt. Die eigentliche Filterung wird jedoch in der Regel im Parallelitätsoperator durchgeführt, der sich auf der Seite der Probe-Eingabe (der Faktentabelle) des Hash-Joins befindet. Wenn der Bitmap-Filter jedoch auf einer Ganzzahlspalte basiert, kann der Filter direkt auf die erste Tabellen- oder Indexscanoperation und nicht auf den Parallelitätsoperator angewendet werden. Diese Technik wird als In-Row-Optimierung bezeichnet.
Ich glaube, das beobachten Sie bei Ihrer Anfrage. Es ist möglich, eine relativ einfache Demo zu erstellen, um einen Operator für Partitionsströme anzuzeigen, der eine Kardinalitätsschätzung reduziert, selbst wenn der Bitmap-Operator IN_ROW
gegen die Faktentabelle verstößt. Datenvorbereitung:
create table outer_tbl (ID BIGINT NOT NULL);
INSERT INTO outer_tbl WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;
create table inner_tbl_1 (ID BIGINT NULL);
create table inner_tbl_2 (ID BIGINT NULL);
INSERT INTO inner_tbl_1 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
INSERT INTO inner_tbl_2 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Hier ist eine Abfrage, die Sie nicht ausführen sollten:
SELECT *
FROM outer_tbl o
INNER JOIN inner_tbl_1 i ON o.ID = i.ID
INNER JOIN inner_tbl_2 i2 ON o.ID = i2.ID
OPTION (HASH JOIN, QUERYTRACEON 9481, QUERYTRACEON 8649);
Ich habe den Plan hochgeladen . Werfen Sie einen Blick auf den Operator in der Nähe von inner_tbl_2
:
Möglicherweise ist auch der zweite Test in Hash-Joins für nullbare Spalten von Paul White hilfreich.
Es gibt einige Inkonsistenzen bei der Anwendung der Zeilenreduzierung. Ich konnte es nur in einem Plan mit mindestens drei Tabellen sehen. Die Reduzierung der erwarteten Zeilen erscheint jedoch bei richtiger Datenverteilung sinnvoll. Angenommen, die verknüpfte Spalte in der Faktentabelle enthält viele wiederholte Werte, die in der Dimensionstabelle nicht vorhanden sind. Ein Bitmap-Filter kann diese Zeilen entfernen, bevor sie den Join erreichen. Für Ihre Abfrage wird die Schätzung auf 1 reduziert. Die Verteilung der Zeilen auf die Hash-Funktion liefert einen guten Hinweis:
Aufgrund dessen vermute ich, dass Sie viele wiederholte Werte für die Object1.Column21
Spalte haben. Wenn die wiederholten Spalten nicht im Statistikhistogramm für enthalten sind, kann Object4.Column19
SQL Server die Kardinalitätsschätzung sehr falsch berechnen.
Ich denke, dass Sie besorgt sein sollten, dass es möglich sein könnte, die Leistung der Abfrage zu verbessern. Wenn die Abfrage die Antwortzeit- oder SLA-Anforderungen erfüllt, ist eine weitere Untersuchung möglicherweise nicht sinnvoll. Wenn Sie jedoch weitere Nachforschungen anstellen möchten, gibt es ein paar Möglichkeiten (außer dem Aktualisieren von Statistiken), um eine Vorstellung davon zu erhalten, ob das Abfrageoptimierungsprogramm einen besseren Plan auswählen würde, wenn es bessere Informationen hätte. Sie könnten die Ergebnisse des Joins zwischen Database1.Schema1.Object10
und Database1.Schema1.Object11
in eine temporäre Tabelle stellen und sehen, ob Sie weiterhin Joins mit verschachtelten Schleifen erhalten. Sie können diesen Join in einen ändern, LEFT OUTER JOIN
damit das Abfrageoptimierungsprogramm die Anzahl der Zeilen in diesem Schritt nicht verringert. Sie können MAXDOP 1
Ihrer Abfrage einen Hinweis hinzufügen , um zu sehen, was passiert. Du könntest benutzenTOP
zusammen mit einer abgeleiteten Tabelle, um den Join zum Letzten zu zwingen, oder Sie können den Join sogar aus der Abfrage auskommentieren. Hoffentlich reichen diese Vorschläge aus, um Ihnen den Einstieg zu erleichtern.
In Bezug auf das Verbindungselement in der Frage ist es äußerst unwahrscheinlich, dass es mit Ihrer Frage zusammenhängt. Dieses Problem hat nichts mit schlechten Zeilenschätzungen zu tun. Dies hat mit einer Racebedingung in Parallelität zu tun, die dazu führt, dass im Abfrageplan hinter den Kulissen zu viele Zeilen verarbeitet werden. Hier sieht es so aus, als würde Ihre Anfrage keine zusätzliche Arbeit leisten.