it-swarm.com.de

Warum führt das Ändern der deklarierten Reihenfolge der Verknüpfungsspalten eine Sortierung ein?

Ich habe zwei Tabellen mit identisch benannten, typisierten und indizierten Schlüsselspalten. Einer von ihnen hat einen einzigartig Clustered-Index, der andere einen nicht eindeutig.

Der Testaufbau

Setup-Skript mit einigen realistischen Statistiken:

DROP TABLE IF EXISTS #left;
DROP TABLE IF EXISTS #right;

CREATE TABLE #left (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE UNIQUE CLUSTERED INDEX IX ON #left (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #left WITH ROWCOUNT=63800000, PAGECOUNT=186000;

CREATE TABLE #right (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE CLUSTERED INDEX IX ON #right (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #right WITH ROWCOUNT=55700000, PAGECOUNT=128000;

Der Repro

Wenn ich diese beiden Tabellen mit ihren Clusterschlüsseln verbinde, erwarte ich einen Eins-zu-Viele-MERGE-Join wie folgt:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.a=r.a AND
    l.b=r.b AND
    l.c=r.c AND
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

Dies ist der Abfrageplan, den ich möchte:

(This is what I want.

(Beachten Sie die Warnungen, sie haben mit den gefälschten Statistiken zu tun.)

Wenn ich jedoch die Reihenfolge der Spalten im Join ändere, wie folgt:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.c=r.c AND     -- used to be third
    l.a=r.a AND     -- used to be first
    l.b=r.b AND     -- used to be second
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

... das passiert:

(The query plan after changing the declared column order in the join.

Der Sortieroperator scheint die Streams gemäß der deklarierten Reihenfolge des Joins zu ordnen, d. H. c, a, b, d, e, f, g, h, Was meinem Abfrageplan eine Blockierungsoperation hinzufügt.

Dinge, die ich mir angesehen habe

  • Ich habe versucht, die Spalten in NOT NULL Zu ändern, die gleichen Ergebnisse.
  • Die ursprüngliche Tabelle wurde mit ANSI_PADDING OFF Erstellt, die Erstellung mit ANSI_PADDING ON Hat jedoch keinen Einfluss auf diesen Plan.
  • Ich habe einen INNER JOIN Anstelle von LEFT JOIN Versucht, keine Änderung.
  • Ich habe es auf einem 2014 SP2 Enterprise entdeckt und einen Repro auf einem 2017 Developer (aktuelle CU) erstellt.
  • Das Entfernen der WHERE-Klausel in der Spalte mit dem führenden Index generiert zwar den guten Plan, wirkt sich jedoch auf die Ergebnisse aus. :)

Schließlich kommen wir zur Frage

  • Ist das beabsichtigt?
  • Kann ich die Sortierung entfernen, ohne die Abfrage zu ändern (das ist Herstellercode, also möchte ich wirklich lieber nicht ...). Ich kann die Tabelle und die Indizes ändern.
41

Ist das beabsichtigt?

Es ist beabsichtigt, ja. Die beste öffentliche Quelle für diese Behauptung ging leider verloren, als Microsoft die Connect-Feedback-Site zurückzog und viele nützliche Kommentare von Entwicklern des SQL Server-Teams auslöschte.

Auf jeden Fall sucht das aktuelle Optimierungsdesign nicht aktiv , um unnötige Sortierungen an sich zu vermeiden . Dies tritt am häufigsten bei Fensterfunktionen und dergleichen auf, kann aber auch bei anderen Operatoren beobachtet werden, die für die Reihenfolge und insbesondere für die Beibehaltung der Reihenfolge zwischen Operatoren empfindlich sind.

Trotzdem kann der Optimierer (in vielen Fällen) unnötige Sortierungen recht gut vermeiden. Dieses Ergebnis tritt jedoch normalerweise aus anderen Gründen auf als dem aggressiven Ausprobieren verschiedener Ordnungskombinationen. In diesem Sinne handelt es sich weniger um den "Suchraum" als vielmehr um die komplexen Wechselwirkungen zwischen orthogonalen Optimierungsmerkmalen, von denen gezeigt wurde, dass sie die allgemeine Planqualität zu akzeptablen Kosten erhöhen.

Beispielsweise kann das Sortieren häufig einfach vermieden werden, indem eine Bestellanforderung (z. B. ORDER BY Auf oberster Ebene) an einen vorhandenen Index angepasst wird. In Ihrem Fall könnte dies bedeuten, dass Sie ORDER BY l.a, l.b, l.c, l.d, l.e, l.f, l.g, l.h; Hinzufügen, dies ist jedoch eine übermäßige Vereinfachung (und inakzeptabel, da Sie die Abfrage nicht ändern möchten).

Allgemeiner kann jede Memogruppe mit erforderlichen oder gewünschten Eigenschaften verknüpft sein, einschließlich der Reihenfolge der Eingabe. Wenn es keinen offensichtlichen Grund gibt, eine bestimmte Anweisung durchzusetzen (z. B. um einen ORDER BY Zu erfüllen oder um korrekte Ergebnisse aus einer Bestellung sicherzustellen - empfindlicher physischer Bediener), es gibt ein Element des Glücks. Ich habe mehr über die Besonderheiten davon geschrieben, da es sich um das Zusammenführen von Joins (im Vereinigungs- oder Join-Modus) in Vermeiden von Sortierungen mit Zusammenführungs-Join-Verkettung handelt. Ein Großteil davon geht über die unterstützte Oberfläche des Produkts hinaus. Behandeln Sie es daher als informativ und können sich ändern.

In Ihrem speziellen Fall können Sie die Indizierung anpassen wie von jadarnel27 vorgeschlagen , um die Sortierungen zu vermeiden. Es gibt jedoch kaum einen Grund, eine Zusammenführung hier vorzuziehen. Sie können auch eine Auswahl zwischen physischer Hash- oder Schleifenverknüpfung mit OPTION(HASH JOIN, LOOP JOIN) mithilfe eines Planleitfadens andeuten, ohne die Abfrage zu ändern, abhängig von Ihrer Kenntnis der Daten und dem Kompromiss zwischen Best, Schlecht und Durchschnitt. Fallleistung.

Beachten Sie schließlich aus Neugier, dass die Sortierungen mit einem einfachen ORDER BY l.b Auf Kosten einer möglicherweise weniger effizienten Viele-zu-Viele-Zusammenführung auf b allein mit einem Komplex vermieden werden können Restwert. Ich erwähne dies hauptsächlich als Beispiel für die Interaktion zwischen den zuvor erwähnten Optimierungsfunktionen und der Art und Weise, wie sich Anforderungen auf höchster Ebene verbreiten können.

30
Paul White 9

Kann ich die Sortierung entfernen, ohne die Abfrage zu ändern (das ist Herstellercode, also möchte ich wirklich lieber nicht ...). Ich kann die Tabelle und die Indizes ändern.

Wenn Sie die Indizes ändern können, ändern Sie die Reihenfolge des Index für #right um der Reihenfolge der Filter im Join zu entsprechen, wird die Sortierung entfernt (für mich):

CREATE CLUSTERED INDEX IX ON #right (c, a, b, d, e, f, g, h)

Überraschenderweise (zumindest für mich) führt dies dazu, dass keine Abfrage mit einer Sortierung endet.

Ist das beabsichtigt?

Wenn man sich die Ausgabe von einige seltsame Trace-Flags ansieht, gibt es einen interessanten Unterschied in der endgültigen Memo-Struktur:

(screenshot of final memo structure for each query

Wie Sie oben in der "Stammgruppe" sehen können, haben beide Abfragen die Option, einen Zusammenführungs-Join als physische Hauptoperation zum Ausführen dieser Abfrage zu verwenden.

Gute Abfrage

Der Join ohne die Sortierung wird von Gruppe 29 Option 1 und Gruppe 31 Option 1 gesteuert (von denen jede Bereichsscans für die beteiligten Indizes sind). Es wird nach Gruppe 27 (nicht gezeigt) gefiltert, bei der es sich um eine Reihe logischer Vergleichsoperationen handelt, die den Join filtern.

Schlechte Abfrage

Die eine mit der Sortierung wird von den (neuen) Optionen 3 gesteuert, die jede dieser beiden Gruppen (29 und 31) hat. Option 3 führt eine physische Sortierung der Ergebnisse der zuvor genannten Entfernungsscans durch (Option 1 jeder dieser Gruppen).

Warum?

Aus irgendeinem Grund steht dem Optimierer in der zweiten Abfrage nicht einmal die Option zur Verfügung, 29.1 und 31.1 direkt als Quellen für den Zusammenführungs-Join zu verwenden. Andernfalls würde es meiner Meinung nach unter den anderen Optionen unter der Stammgruppe aufgeführt. Wenn es überhaupt verfügbar wäre, würde es definitiv diejenigen über die massiv teureren Sortiervorgänge auswählen.

Daraus kann ich nur schließen:

  • dies ist ein Fehler (oder eher eine Einschränkung) im Suchalgorithmus des Optimierers
    • durch Ändern der Indizes und Verknüpfungen auf nur 5 Schlüssel wird die Sortierung für die zweite Abfrage entfernt (6, 7 und 8 Schlüssel haben alle die Sortierung).
    • Dies bedeutet, dass der Suchraum mit 8 Schlüsseln so groß ist, dass der Optimierer einfach keine Zeit hat, die nicht sortierte Lösung als praktikable Option zu identifizieren, bevor sie mit dem Grund "Gut genug gefundener Plan" vorzeitig beendet wird
    • es scheint mir ein wenig fehlerhaft zu sein, dass die Reihenfolge der Verknüpfungsbedingungen den Suchprozess des Optimierers so stark beeinflusst, aber das geht mir wirklich etwas über den Kopf
  • die Sortierung ist erforderlich, um die Richtigkeit der Ergebnisse zu gewährleisten
    • dies scheint unwahrscheinlich, da die Abfrage ohne Sortierung ausgeführt werden kann, wenn weniger Schlüssel vorhanden sind oder die Schlüssel in einer anderen Reihenfolge angegeben werden

Hoffentlich kann jemand mitkommen und erklären , warum die Sortierung erforderlich ist, aber ich fand den Unterschied im Memo-Gebäude interessant genug, um ihn als Antwort zu veröffentlichen.

20
Josh Darnell