it-swarm.com.de

INNER JOIN vs LEFT JOIN-Leistung in SQL Server

Ich habe einen SQL-Befehl erstellt, der INNER JOIN für 9 Tabellen verwendet. Trotzdem dauert dieser Befehl sehr lange (mehr als fünf Minuten). Mein Volk schlug mir vor, INNER JOIN in LEFT JOIN zu ändern, da die Leistung von LEFT JOIN trotz meines Wissens besser ist. Nachdem ich es geändert habe, wurde die Abfragegeschwindigkeit deutlich verbessert.

Ich möchte wissen, warum LEFT JOIN schneller ist als INNER JOIN?

Mein SQL-Befehl sieht wie folgt aus: SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN D und so weiter

pdate: Dies ist eine kurze Darstellung meines Schemas.

FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
    INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
        ON a.CompanyCd = b.CompanyCd 
           AND a.SPRNo = b.SPRNo 
           AND a.SuffixNo = b.SuffixNo 
           AND a.dnno = b.dnno
    INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
        ON a.CompanyCd = h.CompanyCd
           AND a.sprno = h.AcctSPRNo
    INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
        ON c.CompanyCd = h.CompanyCd
           AND c.FSlipNo = h.FSlipNo 
           AND c.FSlipSuffix = h.FSlipSuffix 
    INNER JOIN coMappingExpParty d -- NO PK AND FK
        ON c.CompanyCd = d.CompanyCd
           AND c.CountryCd = d.CountryCd 
    INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
        ON b.CompanyCd = e.CompanyCd
           AND b.ProductSalesCd = e.ProductSalesCd 
    LEFT JOIN coUOM i -- PK = UOMId
        ON h.UOMId = i.UOMId 
    INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
        ON a.CompanyCd = j.CompanyCd
            AND b.BFStatus = j.BFStatus
            AND b.ProductSalesCd = j.ProductSalesCd
    INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
        ON e.ProductGroup1Cd  = g1.ProductGroup1Cd
    INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
        ON e.ProductGroup1Cd  = g2.ProductGroup1Cd
243
Anonymous

Ein LEFT JOIN Ist absolut nicht schneller als ein INNER JOIN. In der Tat ist es langsamer; Per Definition muss ein Outer Join (LEFT JOIN oder RIGHT JOIN) die gesamte Arbeit eines INNER JOIN plus die zusätzliche Arbeit des Null-Erweiterns der Ergebnisse erledigen. Es wäre auch zu erwarten, dass mehr Zeilen zurückgegeben werden, was die Gesamtausführungszeit allein aufgrund der größeren Ergebnismenge weiter erhöht.

(Und selbst wenn ein LEFT JOINwarschneller inbestimmtenSituationen aufgrund einiger schwer zu behebender - Stellen Sie sich vor, die Konfluenz von Faktoren ist funktional nicht gleichbedeutend mit einem INNER JOIN - Sie können also nicht einfach alle Instanzen einer durch die andere ersetzen!)

Höchstwahrscheinlich liegen Ihre Leistungsprobleme an einer anderen Stelle, z. B. wenn ein Kandidatenschlüssel oder ein Fremdschlüssel nicht ordnungsgemäß indiziert wurde. 9 Tische sind eine Menge zu verbinden, so dass die Verlangsamung buchstäblich überall sein könnte. Wenn Sie Ihr Schema veröffentlichen, können wir möglicherweise weitere Details bereitstellen.


Bearbeiten:

Wenn ich weiter darüber nachdenke, könnte ich mir einen Umstand vorstellen, unter dem ein LEFT JOIN Schneller sein könnte als ein INNER JOIN, Und dann:

  • Einige der Tabellen sindsehrklein (z. B. unter 10 Zeilen);
  • Die Tabellen haben nicht genügend Indizes, um die Abfrage abzudecken.

Betrachten Sie dieses Beispiel:

CREATE TABLE #Test1
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test1 (ID, Name) VALUES (1, 'One')
INSERT #Test1 (ID, Name) VALUES (2, 'Two')
INSERT #Test1 (ID, Name) VALUES (3, 'Three')
INSERT #Test1 (ID, Name) VALUES (4, 'Four')
INSERT #Test1 (ID, Name) VALUES (5, 'Five')

CREATE TABLE #Test2
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test2 (ID, Name) VALUES (1, 'One')
INSERT #Test2 (ID, Name) VALUES (2, 'Two')
INSERT #Test2 (ID, Name) VALUES (3, 'Three')
INSERT #Test2 (ID, Name) VALUES (4, 'Four')
INSERT #Test2 (ID, Name) VALUES (5, 'Five')

SELECT *
FROM #Test1 t1
INNER JOIN #Test2 t2
ON t2.Name = t1.Name

SELECT *
FROM #Test1 t1
LEFT JOIN #Test2 t2
ON t2.Name = t1.Name

DROP TABLE #Test1
DROP TABLE #Test2

Wenn Sie dies ausführen und den Ausführungsplan anzeigen, werden Sie feststellen, dass die Abfrage INNER JOIN Tatsächlich mehr kostet als die Abfrage LEFT JOIN, Da sie die beiden oben genannten Kriterien erfüllt. Dies liegt daran, dass SQL Server eine Hash-Übereinstimmung für den INNER JOIN Durchführen möchte, aber geschachtelte Schleifen für den LEFT JOIN Durchführt. Ersteres istnormalerweiseviel schneller, aber da die Anzahl der Zeilen so klein istundgibt es keinen zu verwendenden Index Wenn sich herausstellt, dass die Hashing-Operation der teuerste Teil der Abfrage ist.

Sie können den gleichen Effekt erzielen, indem Sie ein Programm in Ihrer bevorzugten Programmiersprache schreiben, um eine große Anzahl von Suchvorgängen für eine Liste mit 5 Elementen im Vergleich zu einer Hash-Tabelle mit 5 Elementen durchzuführen. Aufgrund der Größe ist die Hash-Tabellenversion tatsächlich langsamer. Aber erhöhen Sie es auf 50 Elemente oder 5000 Elemente, und die Listenversion wird langsamer, weil es O(N) vs. O(1) ist für die Hashtabelle.

Ändern Sie diese Abfrage jedoch in die Spalte ID anstelle von Name, und Sie werden eine ganz andere Geschichte sehen. In diesem Fall werden für beide Abfragen verschachtelte Schleifen ausgeführt, aber die Version INNER JOIN Kann einen der Clustered-Index-Scans durch einen Suchlauf ersetzen. Dies bedeutet, dass dies buchstäblichist. eine Größenordnungschneller mit einer großen Anzahl von Zeilen.

Das Fazit ist also mehr oder weniger das, was ich oben in mehreren Absätzen erwähnt habe. Dies ist mit ziemlicher Sicherheit ein Indexierungs- oder Indexabdeckungsproblem, möglicherweise kombiniert mit einer oder mehreren sehr kleinen Tabellen. Dies sind die einzigen Umstände, unter denen SQL Servermöglicherweisemanchmal einen schlechteren Ausführungsplan für einen INNER JOIN Als einen LEFT JOIN Auswählt.

385
Aaronaught

Es gibt ein wichtiges Szenario, das dazu führen kann, dass eine äußere Verknüpfung schneller ist als eine innere Verknüpfung, die noch nicht erläutert wurde.

Bei Verwendung eines Outer-Joins kann das Optimierungsprogramm die Outer-Join-Tabelle immer aus dem Ausführungsplan löschen, wenn die Join-Spalten die PK der äußeren Tabelle sind und keine der Spalten aus der äußeren Tabelle ausgewählt ist. Zum Beispiel SELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEY Und B.KEY ist die PK für B. Sowohl Oracle (ich glaube, ich habe Release 10 verwendet) als auch SQL Server (ich habe 2008 R2 verwendet) bereinigen Tabelle B aus dem Ausführungsplan.

Dasselbe gilt nicht unbedingt für einen inneren Join: SELECT A.* FROM A INNER JOIN B ON A.KEY=B.KEY Erfordert möglicherweise B im Ausführungsplan, je nachdem, welche Einschränkungen vorliegen.

Wenn A.KEY ein nullwertfähiger Fremdschlüssel ist, der auf B.KEY verweist, kann das Optimierungsprogramm B nicht aus dem Plan entfernen, da es bestätigen muss, dass für jede A-Zeile eine B-Zeile vorhanden ist.

Wenn A.KEY ein obligatorischer Fremdschlüssel ist, der auf B.KEY verweist, kann der Optimierer B aus dem Plan entfernen, da die Einschränkungen die Existenz der Zeile gewährleisten. Nur weil der Optimierer die Tabelle aus dem Plan entfernen kann, heißt das nicht, dass dies der Fall ist. SQL Server 2008 R2 löscht B NICHT aus dem Plan. Oracle 10 löscht B aus dem Plan. In diesem Fall ist leicht zu erkennen, wie die äußere Verknüpfung die innere Verknüpfung in SQL Server übertrifft.

Dies ist ein einfaches Beispiel und für eine eigenständige Abfrage nicht praktisch. Warum sollte man sich an einen Tisch setzen, wenn es nicht nötig ist?

Dies kann jedoch eine sehr wichtige Überlegung beim Entwerfen von Ansichten sein. Häufig wird eine "Alles-tun" -Ansicht erstellt, die alles zusammenführt, was ein Benutzer in Bezug auf eine zentrale Tabelle benötigt. (Insbesondere wenn naive Benutzer Ad-hoc-Abfragen ausführen, die das relationale Modell nicht verstehen.) Die Ansicht enthält möglicherweise alle relevanten Spalten aus vielen Tabellen. Die Endbenutzer greifen jedoch möglicherweise nur auf Spalten aus einer Teilmenge der Tabellen in der Ansicht zu. Wenn die Tabellen mit Outer-Joins verknüpft sind, kann (und kann) der Optimierer die nicht benötigten Tabellen aus dem Plan entfernen.

Es ist wichtig sicherzustellen, dass die Ansicht mit Outer-Joins die richtigen Ergebnisse liefert. Wie Aaronaught gesagt hat - Sie können INNER JOIN nicht blind durch OUTER JOIN ersetzen und dieselben Ergebnisse erwarten. Es gibt jedoch Situationen, in denen dies aus Gründen der Leistung bei der Verwendung von Ansichten hilfreich sein kann.

Eine letzte Anmerkung: Ich habe die Auswirkung auf die Leistung im Lichte der obigen Ausführungen nicht getestet, aber theoretisch sollte es möglich sein, einen INNER JOIN durch einen OUTER JOIN zu ersetzen, wenn Sie auch die Bedingung <FOREIGN_KEY> IS NOT NULL zur where-Klausel.

115
dbenham

Wenn alles so funktioniert, wie es sollte, ABER wir alle wissen, dass alles nicht so funktioniert, wie es sollte, insbesondere wenn es um das Abfrageoptimierungsprogramm, das Zwischenspeichern von Abfrageplänen und Statistiken geht.

Zuerst würde ich vorschlagen, den Index und die Statistiken neu zu erstellen und dann den Cache des Abfrageplans zu leeren, um sicherzustellen, dass dies keine Probleme aufwirft. Allerdings habe ich Probleme gehabt, auch wenn das erledigt ist.

Ich habe einige Fälle erlebt, in denen ein linker Join schneller war als ein innerer Join.

Der Grund dafür ist folgender: Wenn Sie zwei Tabellen haben und eine Spalte mit einem Index (für beide Tabellen) verknüpfen. Die innere Verknüpfung führt unabhängig davon, ob Sie die Einträge im Index in Tabelle 1 durchlaufen und mit dem Index in Tabelle 2 übereinstimmen, zu demselben Ergebnis wie umgekehrt: Durchlaufen Sie die Einträge im Index in Tabelle 2 und stimmen Sie mit dem Index überein in Tabelle eins. Wenn Sie irreführende Statistiken haben, verwendet das Abfrageoptimierungsprogramm die Statistiken des Index, um die Tabelle mit den am wenigsten übereinstimmenden Einträgen zu finden (basierend auf Ihren anderen Kriterien). Wenn Sie zwei Tabellen mit jeweils 1 Million haben, haben Sie in Tabelle eins 10 übereinstimmende Zeilen und in Tabelle zwei 100000 übereinstimmende Zeilen. Der beste Weg wäre, einen Index-Scan für Tabelle 1 durchzuführen und 10-mal in Tabelle 2 abzugleichen. Die Umkehrung wäre ein Index-Scan, der über 100000 Zeilen durchläuft und versucht, 100000 Übereinstimmungen zu finden, und nur 10 sind erfolgreich. Wenn die Statistik nicht korrekt ist, wählt der Optimierer möglicherweise die falsche Tabelle und den falschen Index für die Schleife.

Wenn der Optimierer die linke Verknüpfung in der angegebenen Reihenfolge optimiert, ist die Leistung besser als die der inneren Verknüpfung.

ABER das Optimierungsprogramm kann einen linken Join auch suboptimal als linken Semi-Join optimieren. Um den gewünschten Befehl auszuwählen, können Sie den Befehlshinweis verwenden.

22
Kvasi

Probieren Sie beide Abfragen (die mit innerer und linker Verknüpfung) mit OPTION (FORCE ORDER) am Ende aus und veröffentlichen Sie die Ergebnisse. OPTION (FORCE ORDER) ist ein Abfragehinweis, der das Optimierungsprogramm zwingt, den Ausführungsplan mit der von Ihnen in der Abfrage angegebenen Verknüpfungsreihenfolge zu erstellen.

Wenn INNER JOIN So schnell wie LEFT JOIN Zu arbeiten beginnt, liegt das daran, dass:

  • In einer Abfrage, die ausschließlich aus INNER JOIN Besteht, spielt die Verknüpfungsreihenfolge keine Rolle. Dies gibt dem Abfrageoptimierer die Freiheit, die Verknüpfungen nach Belieben anzuordnen, sodass das Problem möglicherweise auf dem Optimierer beruht.
  • Bei LEFT JOIN Ist dies nicht der Fall, da das Ändern der Verknüpfungsreihenfolge die Ergebnisse der Abfrage verändert. Dies bedeutet, dass die Engine der von Ihnen in der Abfrage angegebenen Verknüpfungsreihenfolge folgen muss, die möglicherweise besser ist als die optimierte.

Ich weiß nicht, ob dies Ihre Frage beantwortet, aber ich war einmal in einem Projekt mit hochkomplexen Berechnungsabfragen, die den Optimierer völlig durcheinander gebracht haben. Wir hatten Fälle, in denen ein FORCE ORDER Die Ausführungszeit einer Abfrage von 5 Minuten auf 10 Sekunden verkürzte.

17
Francisco Pires

Haben eine Reihe von Vergleichen zwischen linken äußeren und inneren Verknüpfungen durchgeführt und konnten keinen konsistenten Unterschied feststellen. Es gibt viele Variablen. Ich arbeite an einer Berichtsdatenbank mit Tausenden von Tabellen, viele mit einer großen Anzahl von Feldern, viele Änderungen im Laufe der Zeit (Herstellerversionen und lokaler Workflow). Es ist nicht möglich, alle Kombinationen von Deckungsindizes zu erstellen, um die Anforderungen einer so großen Vielfalt von Abfragen zu erfüllen und historische Daten zu verarbeiten. Habe gesehen, dass innere Abfragen die Serverleistung beeinträchtigen, weil zwei große (Millionen bis Zehn Millionen von Zeilen) Tabellen durch innere Verknüpfungen eine große Anzahl von Feldern abrufen und kein überdeckender Index vorhanden ist.

Das größte Problem scheint jedoch in den obigen Diskussionen nicht aufzufallen. Möglicherweise ist Ihre Datenbank gut mit Triggern und einer gut konzipierten Transaktionsverarbeitung ausgestattet, um gute Daten zu gewährleisten. Meins hat häufig NULL-Werte, bei denen sie nicht erwartet werden. Ja, die Tabellendefinitionen könnten No-Nulls erzwingen, aber das ist in meiner Umgebung keine Option.

Die Frage ist also: Entwerfen Sie Ihre Abfrage nur auf Geschwindigkeit, eine höhere Priorität für die Transaktionsverarbeitung, bei der derselbe Code tausende Male pro Minute ausgeführt wird. Oder streben Sie nach der Genauigkeit, die eine linke äußere Verknüpfung bietet. Denken Sie daran, dass innere Verknüpfungen auf beiden Seiten Übereinstimmungen finden müssen, sodass ein unerwarteter NULL-Wert nicht nur Daten aus den beiden Tabellen, sondern möglicherweise auch ganze Informationszeilen entfernt. Und es passiert so schön, keine Fehlermeldungen.

Sie können sehr schnell sein, wenn Sie 90% der benötigten Daten abrufen und nicht feststellen, dass die inneren Verknüpfungen Informationen stillschweigend entfernt haben. Manchmal können innere Verknüpfungen schneller sein, aber ich glaube nicht, dass jemand diese Annahme trifft, es sei denn, er hat den Ausführungsplan überprüft. Geschwindigkeit ist wichtig, aber Genauigkeit ist wichtiger.

8
J.O.

Es ist wahrscheinlicher, dass Ihre Leistungsprobleme auf die Anzahl der Joins zurückzuführen sind, die Sie ausführen, und darauf, ob die Spalten, für die Sie beitreten, Indizes aufweisen oder nicht.

Im schlimmsten Fall könnten Sie problemlos 9 vollständige Tabellenscans für jeden Join durchführen.

7
eddiegroves

Äußere Verknüpfungen können in Ansichten eine überlegene Leistung bieten.

Angenommen, Sie haben eine Abfrage, die eine Ansicht enthält, und diese Ansicht besteht aus 10 Tabellen, die miteinander verbunden sind. Angenommen, Ihre Abfrage verwendet nur Spalten aus 3 dieser 10 Tabellen.

Wenn diese 10 Tabellen inner-verknüpft gewesen wären, müsste das Abfrageoptimierungsprogramm sie alle verbinden, obwohl Ihre Abfrage selbst keine 7 benötigt von 10 der Tabellen. Dies liegt daran, dass die inneren Verknüpfungen die Daten möglicherweise selbst herausfiltern und sie für die Berechnung erforderlich machen.

Wenn diese 10 Tabellen stattdessen durch äußere Verknüpfungen miteinander verknüpft worden wären, würde das Abfrageoptimierungsprogramm nur die erforderlichen verknüpfen: 3 von 10 von ihnen in diesem Fall. Dies liegt daran, dass die Joins selbst die Daten nicht mehr filtern und nicht verwendete Joins daher übersprungen werden können.

Quelle: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/

5
MarredCheese

Ich fand etwas Interessantes in SQL Server, als ich überprüfte, ob innere Verknüpfungen schneller sind als linke Verknüpfungen.

Wenn Sie die Elemente der linken verknüpften Tabelle nicht in die select-Anweisung aufnehmen, ist die linke Verknüpfung schneller als dieselbe Abfrage mit innerer Verknüpfung.

Wenn Sie die links verbundene Tabelle in die select-Anweisung aufnehmen, war der innere Join mit derselben Abfrage gleich oder schneller als der linke Join.

2
Buzzzzzzz