Warum sortiert mein ORDER BY zwei Tabellen vor dem EXCEPT (langsam) und nicht nach dem EXCEPT (schnell)?

12

SQL Server 2008 R2-Abfrageoptimierungspuzzlespiel

Wir haben zwei Tabellen mit jeweils 9 Millionen Zeilen. 70.000 Zeilen sind unterschiedlich, die anderen sind gleich.

Das ist schnell, 13 Sekunden,

select * from bigtable1
except select * from similar_bigtable2

Dies sortiert die Ausgabe und ist auch schnell, auch 13 Sekunden,

select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column

Das ist zwar enorm langsam:

;with q as (
    select * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

Und selbst ein "Trick", den ich manchmal verwende, um SQL Server darauf hinzuweisen, dass er einen bestimmten Teil der Abfrage vorab berechnen muss, funktioniert nicht und führt auch zu langsamen Abfragen:

;with q as (
    select top 100 percent * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

In den Abfrageplänen ist der Grund nicht schwer zu finden:

Abfrageplan Abfrageplan mit ORDER BY

SQL Server platziert zwei Arten von 9 Millionen Zeilen vor dem Hashmatch, während ich es vorziehen würde, nur eine Art von 70.000 Zeilen nach dem Hashmatch hinzuzufügen.

Also die Frage: Wie kann ich das Abfrageoptimierungsprogramm dazu anweisen?

thomaspaulb
quelle
3
Es wird nicht vor dem Hashmatch sortiert, sondern es wird sortiert und anschließend ein Merge-Join (kein Hash-Join) ausgeführt. Vielleicht gibt es einen Hinweis, einen Hash-Join zu erzwingen (oder einen Merge-Join zu verhindern)?
Thilo
3
Offenbar hat das SQL Server-Abfrageoptimierungsprogramm festgestellt, dass das Sortieren der Daten von Vorteil ist, sodass der viel schnellere Merge-Join (der nur für sortierte Daten funktioniert) anstelle des viel langsameren Hash-Match-
Joins
9
Haben Sie Alternativen zu EXCEPT(zB OUTER JOIN) ausprobiert ? Mir ist klar, dass die Syntax weniger praktisch ist, aber Sie können dort möglicherweise besser mit Index- / Verknüpfungshinweisen spielen (oder müssen dies nicht). Die Alternative, die Sie jetzt verwenden (Zeug zuerst in eine #temp-Tabelle), ist eine Umgehung des letzten Auswegs, aber in einigen Fällen ist es die einzige Möglichkeit, das Optimierungsprogramm zu zwingen, zwei Teile einer Abfrage auf die von Ihnen gewünschte Weise vollständig zu trennen.
Aaron Bertrand

Antworten:

1

Der Hauptunterschied zwischen diesen beiden Abfrageplänen liegt in der Differenz zwischen Hash Match und Merge Join. Hash Match ist effizienter und wie Sie sehen, wird die Abfrage in Option 1 schneller ausgeführt (ohne CTE).

CTE ist ein großartiges Tool, aber es scheint in zwei Fällen nicht effizient zu sein: Komplexe Prädikate oder nicht eindeutiger Eltern- / Kind-Schlüssel. In Ihrem Fall gibt es keinen eindeutigen Schlüssel und der SQL Server muss die Datensätze zuerst sortieren, um Ihre Anforderung erfüllen zu können. Schauen Sie sich den folgenden Link an, um mehr über dieses Problem zu erfahren : http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx

Entweder müssen Sie die Langsamkeit akzeptieren oder die Logik mit der WHILE-Schleife neu schreiben, was effizienter sein kann.

Himmel
quelle
0

Versuchen Sie das mal, besser?

select * from
(
    select * from bigtable1
    except 
    select * from similar_bigtable2
) t
order by sort_column
Gordon Bell
quelle
0

Dies ist keine ideale Lösung, aber wenn Sie nicht in der Lage sind, das tsql zu strukturieren, um einen effizienten Plan zu generieren, können Sie einen Planleitfaden festlegen, um den gewünschten Plan zu erzwingen. Dies würde bedeuten, dass wenn ein effizienterer Plan verfügbar wird, SQL dies nicht berücksichtigt, dies jedoch eine Option ist.

cfradenburg
quelle