SQL Performance UNION vs OR

70

Ich habe gerade einen Teil eines Optimierungsartikels gelesen und bin bei der folgenden Aussage fehlerhaft vorgegangen :

Bei Verwendung von SQL ersetzen Sie Anweisungen ORmit UNION:

select username from users where company = ‘bbc’ or company = ‘itv’;

zu:

select username from users where company = ‘bbc’ union
select username from users where company = ‘itv’;

Von einem schnellen EXPLAIN:

Verwenden von OR:

Geben Sie hier die Bildbeschreibung ein

Verwenden von UNION:

Geben Sie hier die Bildbeschreibung ein

Bedeutet das nicht, dass UNIONsich die Arbeit verdoppelt ?

Während ich schätzen UNIONfür bestimmte RDBMSes und bestimmte Tabellenschemata können mehr performant, ist dies nicht kategorisch wahr wie der Autor Vorschläge.

Frage

Liege ich falsch?

Jason McCreary
quelle
1
Ich würde denken, dass UNIONdies mehr Arbeit bedeutet, da die Duplikate entfernt werden müssen, wobei der Filter die angegebenen Kriterien abrufen würde. Ich bin überrascht, dass die Verwendung nicht empfohlen wird IN.
Kermit
3
Es kann wahr sein, wenn mysql kippe Indizes verwenden , wenn es ORin WHEREKlausel.
Igor Romanchenko
@Igor Romanchenko, unterstütze dies mit einer Antwort.
Jason McCreary
Ich würde vermuten, dass der Abfrageanalysator die Anweisungen oder in eine IN-Anweisung
Darren Kopp
@ Darren Kopp, ich bin mir ziemlich sicher, dass es umgekehrt ist. Zumindest in MySQL.
Jason McCreary

Antworten:

114

Entweder hat der Artikel, den Sie gelesen haben, ein schlechtes Beispiel verwendet, oder Sie haben ihren Punkt falsch interpretiert.

select username from users where company = 'bbc' or company = 'itv';

Dies entspricht:

select username from users where company IN ('bbc', 'itv');

MySQL kann companyfür diese Abfrage einen Index verwenden . Es ist nicht nötig, eine UNION zu machen.

Der schwierigere Fall ist, wenn Sie eine ORBedingung haben, die zwei verschiedene Spalten umfasst.

select username from users where company = 'bbc' or city = 'London';

Angenommen, es gibt einen Index companyund einen separaten Index city. Welchen Index sollte MySQL normalerweise verwenden, da es in einer bestimmten Abfrage normalerweise nur einen Index pro Tabelle verwendet? Wenn der Index aktiviert ist company, muss immer noch ein Tabellenscan durchgeführt werden, um Zeilen zu finden, in denen citysich London befindet. Wenn der Index aktiviert ist city, müsste ein Tabellenscan nach Zeilen durchgeführt werden, in denen companybbc steht.

Die UNIONLösung ist für diese Art von Fall.

select username from users where company = 'bbc' 
union
select username from users where city = 'London';

Jetzt kann jede Unterabfrage den Index für ihre Suche verwenden, und die Ergebnisse der Unterabfrage werden durch die kombiniert UNION.


Ein anonymer Benutzer schlug eine Bearbeitung meiner obigen Antwort vor, aber ein Moderator lehnte die Bearbeitung ab. Es sollte ein Kommentar gewesen sein, keine Bearbeitung. Die Behauptung der vorgeschlagenen Bearbeitung lautete, dass UNION die Ergebnismenge sortieren muss, um doppelte Zeilen zu entfernen. Dadurch wird die Abfrage langsamer ausgeführt, und die Indexoptimierung ist daher eine Wäsche.

Meine Antwort ist, dass die Indizes dazu beitragen, die Ergebnismenge auf eine kleine Anzahl von Zeilen zu reduzieren, bevor die UNION stattfindet. UNION eliminiert zwar Duplikate, muss dazu jedoch nur die kleine Ergebnismenge sortieren. Es kann Fälle geben, in denen die WHERE-Klauseln mit einem wesentlichen Teil der Tabelle übereinstimmen und das Sortieren während UNION genauso teuer ist wie das einfache Durchführen des Tabellenscans. Es ist jedoch üblicher, dass die Ergebnismenge durch die indizierten Suchvorgänge reduziert wird, sodass die Sortierung viel kostengünstiger ist als der Tabellenscan.

Der Unterschied hängt von den Daten in der Tabelle und den gesuchten Begriffen ab. Die einzige Möglichkeit, die beste Lösung für eine bestimmte Abfrage zu ermitteln, besteht darin, beide Methoden im MySQL-Abfrageprofiler auszuprobieren und ihre Leistung zu vergleichen.

Bill Karwin
quelle
1
Das Zitat, das ich zur Verfügung stellte, war das genaue Beispiel im Artikel. Es gab also nichts zu falsch zu interpretieren. Ich wusste , mit UNIONvs. ORwar nicht kategorisch wahr . Aber ich markiere dies als richtig, da es das ursprüngliche Beispiel als falsch ansprach und gleichzeitig einen Anwendungsfall dessen lieferte, was der Autor wahrscheinlich meinte .
Jason McCreary
Leider hat der Autor möglicherweise über eine Lösung geschrieben, ohne die Fälle zu verstehen, in denen die Lösung nützlich ist und nicht benötigt wird. Oder er hat sein Wissen auf eine alte Version von MySQL gestützt, die auch IN()Prädikate nicht optimiert hat .
Bill Karwin
@BillKarwin Wenn die zwei verschiedenen Spalten indiziert sind, führt MySQL dann keine "Index Merge Optimization" durch, um das Ergebnis einzelner Scans basierend auf beiden Indizes zusammenzuführen?
Sactiw
@sactiw, manchmal. In der Praxis habe ich festgestellt, dass der Optimierer die Indexzusammenführung nicht verwendet, wenn man dies erwarten würde, daher verlasse ich mich nicht darauf.
Bill Karwin
1
Ich verstehe endlich die Notwendigkeit der UNION. Vielen Dank! Ich bestelle Ihr Buch bei Amazon.
Isapir
5

Das sind nicht die gleichen Abfragen.

Ich habe nicht viel Erfahrung mit MySQL, daher bin ich mir nicht sicher, was der Abfrageoptimierer tut oder nicht, aber hier sind meine Gedanken aus meinem allgemeinen Hintergrund (hauptsächlich MS SQL Server).

In der Regel kann der Abfrageanalysator die beiden oben genannten Abfragen übernehmen und aus ihnen genau den gleichen Plan erstellen (wenn sie gleich wären), sodass dies keine Rolle spielt. Ich würde vermuten, dass es keinen Leistungsunterschied zwischen diesen Abfragen gibt (die gleichwertig sind).

select distinct username from users where company = ‘bbc’ or company = ‘itv’;

und

select username from users where company = ‘bbc’ 
union
select username from users where company = ‘itv’;

Die Frage ist nun, ob es einen Unterschied zwischen den folgenden Abfragen gibt, von denen ich eigentlich nichts weiß, aber ich würde vermuten, dass der Optimierer es eher wie die erste Abfrage machen würde

select username from users where company = ‘bbc’ or company = ‘itv’;

und

select username from users where company = ‘bbc’ 
union all
select username from users where company = ‘itv’;
Darren Kopp
quelle
1
+ über die Abfragen, die nicht gleich sind. Trotzdem UNION ALLergibt sich immer noch das gleiche EXPLAINwie UNION.
Jason McCreary
UNION ALList normalerweise schneller als UNION. Letzteres impliziert UNION DISTINCT, wodurch ein De-Dup-Durchlauf über eine temporäre Tabelle erforderlich ist. Neuere Versionen vermeiden in bestimmten Situationen die temporäre Tabelle und helfen so mehr. Das orBeispiel, das Sie haben, ist immer schneller, weil es verwenden kannINDEX(company)
Rick James
2

Dies hängt davon ab, was der Optimierer letztendlich tut, basierend auf der Größe der Daten, Indizes, Softwareversion usw.

Ich würde vermuten, dass die Verwendung von OR dem Optimierer eine bessere Chance gibt, einige Effizienzvorteile zu erzielen, da alles in einer einzigen logischen Anweisung enthalten ist.

Außerdem hat UNION einen gewissen Overhead, da ein Reset- Set erstellt wird (keine Duplikate). Jede Anweisung in der UNION sollte ziemlich schnell ausgeführt werden, wenn das Unternehmen indiziert ist ... nicht sicher, ob es wirklich die doppelte Arbeit leisten würde.

Endeffekt

Wenn Sie nicht wirklich das brennende Bedürfnis haben, jede Geschwindigkeit aus Ihrer Abfrage herauszuholen, ist es wahrscheinlich besser, einfach das Formular zu verwenden, das Ihre Absicht am besten kommuniziert ... den OP

Aktualisieren

Ich wollte auch IN erwähnen. Ich glaube, dass die folgende Abfrage eine bessere Leistung als der OP liefert (es ist auch die Form, die ich bevorzuge):

select username from users where company in ('bbc', 'itv');

David J.
quelle
0

In fast allen Fällen führt die Version unionoder union allzwei vollständige Tabellenscans der Benutzertabelle durch.

Die orVersion ist in der Praxis viel besser, da die Tabelle nur einmal gescannt wird. Es wird auch nur einmal ein Index verwendet, falls verfügbar.

Die ursprüngliche Aussage scheint einfach falsch zu sein, für nahezu jede Datenbank und jede Situation.

Gordon Linoff
quelle
Um ganz klar zu sein, UNIONwird auch ein Index verwendet, falls verfügbar. Es werden jedoch beide Tabellen gescannt . Nur ein kleinerer Datensatz führt sie dann wieder zusammen.
Jason McCreary
nein-nein-nein, wenn Sie or/ verwenden, werden inSie "Indexbereichsscan" verwenden, und im Fall von union/ werden union allSie non-uniqueoder sogar primary key lookupplus verwendenindex merge
Jewgenij Afanasjew
@ YevgeniyAfanasyev. . . Die Erklärungsergebnisse sind ziemlich klar, dass es keine Indizes in der Tabelle gibt.
Gordon Linoff
Danke für deinen Kommentar. Die Frage gibt nicht die Bedingung "Keine Indizes in der Tabelle" an, wie Sie antworten. Wenn Sie dies zu Beginn Ihrer Antwort setzen würden, würde dies Leuten wie mir helfen, nach ihren Fallszenarien zu suchen.
Jewgenij Afanasjew
INund ORsind die gleichen. Sie können dies sehen, indem Sie EXPLAINsehen, dass der Optimierer einen in den anderen verwandelt.
Rick James
-1

Die Antwort von Bill Karwin ist ziemlich richtig. Wenn beide Teile der OR-Anweisung einen eigenen Index haben, ist es besser, Union zu machen, da es einfacher ist, sie zu sortieren und Duplikate zu entfernen, wenn Sie nur eine kleine Teilmenge der Ergebnisse haben. Die Gesamtkosten sind fast geringer als die Verwendung nur eines Index (für eine der Spalten) und des Tabellenscans für die andere Spalte (da MySQL nur einen Index für eine Spalte verwendet).

Es hängt von der Struktur und den Bedürfnissen der Tabelle im Allgemeinen ab, aber bei großen Tabellen hat mir die Vereinigung bessere Ergebnisse gebracht.

Çağatay Gürtürk
quelle
Sie sagten mysql only uses one index for one column- es ist nicht wahr. Sie können Ihre Spalte in vielen Indizes haben.
Jewgenij Afanasjew
Während der Ausführung einer Abfrage verwendet MySQL nur einen Index für eine einzelne Spalte. Es geht nicht darum, mehrere Indizes für eine Spalte zu definieren.
Çağatay Gürtürk
Dies ist nicht wieder wahr. MySQL verwendet einen Index für eine Abfrage, nicht für eine Spalte.
Jewgenij Afanasjew
Sagen wir es noch genauer: "ein Index pro SELECT". (Dies vermeidet die Mehrdeutigkeit, ob a UNIONeine oder mehrere "Abfragen" ist.)
Rick James