SQL tritt Vs SQL-Unterabfragen bei (Leistung)?

110

Ich möchte wissen, ob ich eine Join- Abfrage wie diese habe -

Select E.Id,E.Name from Employee E join Dept D on E.DeptId=D.Id

und eine Unterabfrage wie diese -

Select E.Id,E.Name from Employee Where DeptId in (Select Id from Dept)

Wenn ich die Leistung betrachte, welche der beiden Abfragen wäre schneller und warum ?

Gibt es auch eine Zeit, in der ich eine der anderen vorziehen sollte?

Tut mir leid, wenn dies zu trivial ist und vorher gefragt wurde, aber ich bin verwirrt darüber. Außerdem wäre es großartig, wenn ihr mir Tools vorschlagen könnt, mit denen ich die Leistung von zwei Abfragen messen sollte. Vielen Dank!

Vishal
quelle
5
@Lucero, diese Frage ist mit sql-server-2008 gekennzeichnet, wobei der von Ihnen erwähnte Beitrag mit MySql gekennzeichnet ist. Sie können daraus schließen, dass die Antworten gleich sind. Die Leistungsoptimierung wird bei den beiden RDBMS unterschiedlich durchgeführt.
Francois Botha

Antworten:

48

Ich würde erwarten, dass die erste Abfrage schneller ist, hauptsächlich weil Sie eine Äquivalenz und einen expliziten JOIN haben. Nach meiner Erfahrung INist dies ein sehr langsamer Operator, da SQL ihn normalerweise als eine Reihe von WHEREKlauseln auswertet , die durch "ODER" ( WHERE x=Y OR x=Z OR...) getrennt sind.

Wie bei ALL THINGS SQL kann Ihr Kilometerstand jedoch variieren. Die Geschwindigkeit hängt unter anderem stark von Indizes ab (haben Sie Indizes für beide ID-Spalten? Das hilft sehr ...).

Die einzige WIRKLICHE Möglichkeit, mit 100% iger Sicherheit festzustellen, was schneller ist, besteht darin, die Leistungsverfolgung zu aktivieren (E / A-Statistik ist besonders nützlich) und beide auszuführen. Stellen Sie sicher, dass Sie Ihren Cache zwischen den Läufen leeren!

JNK
quelle
16
Ich habe ernsthafte Zweifel an dieser Antwort, da die meisten DBMS, definitiv SQL Server 2008 und höher, die einzelne ID-Unterabfrage (nicht korreliert, dh nicht auf mehrere äußere Abfragespalten verweisen) in einen relativ schnellen Semi-Join übersetzen. Wie bereits in einer anderen Antwort erwähnt, gibt der erste echte Join eine Zeile für JEDES Auftreten der übereinstimmenden ID in Dept zurück. Dies macht keinen Unterschied für eine eindeutige ID, gibt Ihnen jedoch Tonnen von Duplikaten an anderer Stelle. Das Aussortieren mit DISTINCT oder GROUP BY ist eine weitere schwere Leistungsbelastung. Überprüfen Sie die Ausführungspläne in SQL Server Management Studio!
Erik Hart
2
Die IN-Klausel als Äquivalent zu OR gilt für Parameter- / Wertelisten, jedoch nicht für Unterabfragen, die meist wie Joins behandelt werden.
Erik Hart
42

Nun, ich glaube, es ist eine "Alt aber Gold" -Frage. Die Antwort lautet: "Es kommt darauf an!". Die Aufführungen sind ein so heikles Thema, dass es zu dumm wäre zu sagen: "Niemals Unterabfragen verwenden, immer mitmachen". Unter den folgenden Links finden Sie einige grundlegende Best Practices, die ich als sehr hilfreich empfunden habe:

Ich habe eine Tabelle mit 50000 Elementen, das Ergebnis, das ich suchte, war 739 Elemente.

Meine Frage war zunächst:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND p.anno = (
    SELECT MAX(p2.anno) 
    FROM prodotto p2 
    WHERE p2.fixedId = p.fixedId 
)

Die Ausführung dauerte 7,9 Sekunden.

Meine Frage lautet endlich:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND (p.fixedId, p.anno) IN
(
    SELECT p2.fixedId, MAX(p2.anno)
    FROM prodotto p2
    WHERE p.azienda_id = p2.azienda_id
    GROUP BY p2.fixedId
)

und es dauerte 0,0256s

Gutes SQL, gut.

linuxatico
quelle
3
Interessant, können Sie erklären, wie das Hinzufügen der GROUP BY das Problem behoben hat?
Cozos
6
Die von der Unterabfrage generierte temporäre Tabelle war kleiner. Daher ist die Ausführung schneller, da weniger Daten
eingecheckt werden müssen
2
Ich denke, dass Sie in der ersten Abfrage eine gemeinsame Variable zwischen äußerer Abfrage und Unterabfrage haben. In jeder Zeile der Hauptabfrage wird die Unterabfrage ausgeführt, in der zweiten wird die Unterabfrage nur einmal ausgeführt, und auf diese Weise wird die Leistung verbessert.
Ali Faradjpour
1
SQL Server und MySQL und ... SQL (mit Ausnahme von NoSQL) sind in der Infrastruktur so ähnlich. Wir haben eine Art Abfrageoptimierungs-Engine, unter der die IN (...) - Klauseln in Join konvertiert werden (falls dies möglich war). Wenn Sie jedoch eine Gruppe in einer gut indizierten Spalte haben (basierend auf ihrer Kardinalität), ist diese viel schneller. Es kommt also wirklich auf die Situation an.
Alix
10

Sehen Sie sich die Ausführungspläne an, um festzustellen, wie unterschiedlich der SQl-Server sie interpretiert. Sie können Profiler auch verwenden, um die Abfragen tatsächlich mehrmals auszuführen und den Unterschied zu ermitteln.

Ich würde nicht erwarten, dass diese so schrecklich unterschiedlich sind, wenn Sie echte, große Leistungssteigerungen bei der Verwendung von Joins anstelle von Unterabfragen erzielen, wenn Sie korrelierte Unterabfragen verwenden.

EXISTS ist oft besser als eine dieser beiden und wenn Sie über linke Joins sprechen, bei denen Sie alle Datensätze möchten, die nicht in der linken Join-Tabelle enthalten sind, ist NOT EXISTS oft eine viel bessere Wahl.

HLGEM
quelle
9

Die Leistung basiert auf der Datenmenge, mit der Sie ...

Wenn es weniger Daten um 20k sind. JOIN funktioniert besser.

Wenn die Daten eher 100k + entsprechen, funktioniert IN besser.

Wenn Sie die Daten aus der anderen Tabelle nicht benötigen, ist IN gut, aber es ist immer besser, EXISTS zu wählen.

Alle diese Kriterien habe ich getestet und die Tabellen haben die richtigen Indizes.

JP Emvia
quelle
4

Die Leistung sollte gleich sein; Es ist viel wichtiger, dass die richtigen Indizes und Clustering auf Ihre Tabellen angewendet werden ( zu diesem Thema gibt es einige gute Ressourcen ).

(Bearbeitet, um die aktualisierte Frage widerzuspiegeln)

Lucero
quelle
4

Die beiden Abfragen sind möglicherweise nicht semantisch äquivalent. Wenn ein Mitarbeiter für mehr als eine Abteilung arbeitet (möglich in dem Unternehmen, für das ich arbeite; dies würde zugegebenermaßen bedeuten, dass Ihre Tabelle nicht vollständig normalisiert ist), würde die erste Abfrage doppelte Zeilen zurückgeben, während die zweite Abfrage dies nicht tun würde. Um die Abfragen in diesem Fall gleichwertig zu machen, DISTINCTmüsste das Schlüsselwort zur SELECTKlausel hinzugefügt werden , was sich auf die Leistung auswirken kann.

Beachten Sie, dass es eine Entwurfsregel gibt, die besagt, dass eine Tabelle eine Entität / Klasse oder eine Beziehung zwischen Entitäten / Klassen modellieren soll, jedoch nicht beide. Daher schlage ich vor, dass Sie beispielsweise eine dritte Tabelle erstellen OrgChart, um die Beziehung zwischen Mitarbeitern und Abteilungen zu modellieren.

eines Tages, wenn
quelle
4

Ich weiß, dass dies ein alter Beitrag ist, aber ich denke, dass dies ein sehr wichtiges Thema ist, insbesondere heutzutage, wo wir über 10 Millionen Datensätze haben und über Terabytes an Daten sprechen.

Ich werde auch auf die folgenden Beobachtungen eingehen. Ich habe ungefähr 45 Millionen Datensätze in meiner Tabelle ([Daten]) und ungefähr 300 Datensätze in meiner [Katzen] -Tabelle. Ich habe eine umfangreiche Indizierung für alle Abfragen, über die ich sprechen werde.

Betrachten Sie Beispiel 1:

UPDATE d set category = c.categoryname
FROM [data] d
JOIN [cats] c on c.id = d.catid

versus Beispiel 2:

UPDATE d set category = (SELECT TOP(1) c.categoryname FROM [cats] c where c.id = d.catid)
FROM [data] d

Beispiel 1 dauerte ungefähr 23 Minuten, um zu laufen. Beispiel 2 dauerte ungefähr 5 Minuten.

Daher würde ich zu dem Schluss kommen, dass die Unterabfrage in diesem Fall viel schneller ist. Denken Sie natürlich daran, dass ich M.2-SSD-Laufwerke verwende, die E / A mit 1 GB / s unterstützen (das sind Bytes, keine Bits), daher sind meine Indizes auch sehr schnell. Dies kann sich also unter Ihren Umständen auch auf die Geschwindigkeit auswirken

Wenn es sich um eine einmalige Datenbereinigung handelt, lassen Sie sie wahrscheinlich am besten laufen und beenden. Ich verwende TOP (10000) und sehe, wie lange es dauert, und multipliziere mit der Anzahl der Datensätze, bevor ich die große Abfrage treffe.

Wenn Sie Produktionsdatenbanken optimieren, würde ich dringend empfehlen, Daten vorzuverarbeiten, dh Trigger oder Job-Broker zu verwenden, um Aktualisierungsdatensätze zu asynchronisieren, damit der Echtzeitzugriff statische Daten abruft.

Arvin Amir
quelle
0

Sie können einen Erklärungsplan verwenden, um eine objektive Antwort zu erhalten.

Für Ihr Problem würde ein Exists-Filter wahrscheinlich die schnellste Leistung erbringen.

Snekse
quelle
2
"Ein Exists-Filter würde wahrscheinlich die schnellste Leistung erbringen" - wahrscheinlich nicht, denke ich, obwohl eine endgültige Antwort einen Test mit den tatsächlichen Daten erfordern würde. Vorhandene Filter sind wahrscheinlich schneller, wenn mehrere Zeilen mit denselben Suchwerten vorhanden sind. Ein vorhandener Filter wird daher möglicherweise schneller ausgeführt, wenn bei der Abfrage überprüft wurde, ob andere Mitarbeiter aus derselben Abteilung erfasst wurden, wahrscheinlich jedoch nicht, wenn nach einer Abteilung gesucht wird Tabelle.
Würde es in diesem letzten Szenario langsamer laufen?
Snekse
Es würde vom Optimierer abhängen - unter bestimmten Umständen könnte es sein, aber normalerweise würde ich eine sehr ähnliche Leistung erwarten.