Kann mir jemand bei diesem schrecklichen Abfrageplan helfen?

7

Die Abfrage:

    SELECT Object1.Column1, Object2.Column2 AS Column3, Object2.Column4 AS Column5, 
Object3.Column6, Object3.Column7,Object1.Column8, Object1.Column9, 
Object1.Column10, Object1.Column11, Object1.Column12, Object1.Column13, 
Object1.Column14, Object1.Column15 as Column15, Object1.Column16, 
Object4.Column4 AS Column17, Object4.Column2 AS Column18, Object1.Column19,
 Object1.Column20, Object1.Column21, Object1.Column22, Object1.Column23, 
Object1.Column24, Object1.Column25, Object1.Column26, Object5.Column4, 
Object1.Column27, Object1.Column28, Object1.Column29, Object3.Column30, 
Object3.Column1 as Column31, Object3.Column32 as Column33, Object1.Column34 
as Column34, ? AS Column35 , Object3.Column36 as Column37 
FROM Object6 AS Object1 
INNER JOIN Object7 AS Object3 ON Object1.Column38 = Object3.Column1 
INNER JOIN Object8 AS Object2 ON Object3.Column30 = Object2.Column1 
LEFT JOIN Object9 AS Object4 ON Object1.Column16 = Object4.Column2 
LEFT JOIN Object10 AS Object5 ON Object1.Column9 = Object5.Column2 
WHERE Object2.Column1 <> ? AND Object1.Column8 = ? 
AND ( coalesce(Column16,?)= ? ) 
AND EXISTS (
         SELECT ? 
         FROM Object11 
         WHERE Column39 = ? 
         AND Column30 = Object3.Column30) 
ORDER BY Column7 desc
OFFSET ? ROWS FETCH FIRST ? ROWS ONLY

Hier ist der Abfrageplan

Ich weiß, dass ich vielleicht einen Index dazu hinzufügen sollte:

Database1.Schema1.Object7.Column30, Database1.Schema1.Object7.Column36, Database1.Schema1.Object7.Column6, Database1.Schema1.Object7.Column32

Eine dieser Spalten ist jedoch eine Varchar 4000 und kann aufgrund der großen Dimension des Feldes nicht erstellt werden.

Ich habe festgestellt, dass es nur 25 Sekunden dauert, wenn die zurückgegebenen Zeilen kleiner sind als die erste Abrufnummer

Gabriele D'Onufrio
quelle
Hallo, versuchen Sie, OPTION(FORCE ORDER) zu Ihrer Anfrage hinzuzufügen
Denis Rubashkin

Antworten:

9

Geben Sie hier die Bildbeschreibung ein

Der Ausführungsplan greift Object7zuerst mit einem nicht abdeckenden Index der Column7Reihe nach zu. Anschließend werden einige Schlüsselsuchen für diese Tabelle durchgeführt und verschachtelte Schleifen werden mit den anderen Tabellen verknüpft, wobei die endgültige Verknüpfung zu dem TOPOperator führt, der noch von bestellt wurde Column7.

Sobald dies genügend Zeilen empfangen hat, um die OFFSET ... FETCHAnforderungen zu erfüllen , kann es aufhören, weitere Zeilen von nachgeschalteten Betreibern anzufordern. SQL Server schätzt, dass ab dem ersten Index nur 2419 Zeilen gelesen werden müssen, Object7.Column7bevor dieser Punkt erreicht ist.

Diese Schätzung ist überhaupt nicht korrekt. Tatsächlich liest es die Gesamtheit Object7aller Zeilen und es gehen ihnen wahrscheinlich die Zeilen aus, bevor das OFFSET ... FETCHerfüllt ist.

Der Semi-Join-On Object11reduziert die Anzahl der Zeilen um fast die Hälfte, aber der Killer ist der Join-On Object6und das Prädikat auf demselben Tisch. Zusammen reduzieren diese die 9,753,116aus dem Semijoin kommenden Reihen auf 2.

Sie können versuchen, einige Zeit mit Statistiken zu den beteiligten Tabellen zu verbringen, um zu versuchen, die Kardinalitätsschätzungen aus diesen Verknüpfungen genauer zu ermitteln, oder Sie können hinzufügen, OPTION (USE HINT ('DISABLE_OPTIMIZER_ROWGOAL') )dass der Plan kostenpflichtig ist, ohne davon auszugehen, dass er aufgrund dessen OFFSET ... FETCH- vorzeitig beendet werden kann wird Ihnen sicherlich einen anderen Plan geben.

Martin Smith
quelle
1

Wenn Sie einen Index für Object11, Column39 + Column30 und einen Index für Object7, Column30 mit anderen Feldern von Object7 im INCLUDETeil der CREATE INDEXAnweisung für Object 7 hinzufügen können , sollte die Leistung erheblich gesteigert werden. Dies ist die überwiegende Mehrheit der mit dieser Abfrage verbundenen Ressourcenausgaben.

Basierend auf dem XML des Plans scheinen diese nahe an den optimalen Indizes für diese Abfrage zu liegen:

CREATE INDEX Idx_Object11_Column39_Column30
ON Object11(Column39_Column30)

CREATE INDEX Idx_Object7_Column30_Column1_Includes
ON Object7 (Column30, Column1)
INCLUDE (Column7, Column36, Column6, Column2)
Lachender Vergil
quelle