Mysql
 sql >> Datenbank >  >> RDS >> Mysql

SQL UNION ALL, um Duplikate zu eliminieren

Aber im Beispiel hat die erste Abfrage eine Bedingung für die Spalte a , während die zweite Abfrage eine Bedingung in Spalte b hat . Dies kam wahrscheinlich von einer Abfrage, die schwer zu optimieren ist:

SELECT * FROM mytable WHERE a=X OR b=Y

Diese Abfrage ist mit einfacher B-Tree-Indizierung schwer zu optimieren. Durchsucht die Engine einen Index in Spalte a ? Oder in Spalte b ? In beiden Fällen erfordert die Suche nach dem anderen Begriff einen Tabellenscan.

Daher der Trick, UNION zu verwenden, um in zwei Abfragen für jeweils einen Begriff zu trennen. Jede Unterabfrage kann den besten Index für jeden Suchbegriff verwenden. Kombinieren Sie dann die Ergebnisse mit UNION.

Die beiden Teilmengen können sich jedoch überschneiden, da in einigen Zeilen b=Y gilt kann auch a=X haben in diesem Fall treten solche Zeilen in beiden Teilmengen auf. Daher müssen Sie eine doppelte Eliminierung durchführen, sonst sehen Sie im Endergebnis einige Zeilen doppelt.

SELECT * FROM mytable WHERE a=X 
UNION DISTINCT
SELECT * FROM mytable WHERE b=Y

UNION DISTINCT ist teuer, da typische Implementierungen die Zeilen sortieren, um Duplikate zu finden. Genauso wie wenn Sie SELECT DISTINCT ... verwenden .

Wir haben auch den Eindruck, dass es noch mehr "verschwendete" Arbeit ist, wenn die zwei Teilmengen von Zeilen, die Sie vereinigen, viele Zeilen in beiden Teilmengen enthalten. Es sind viele Zeilen zu beseitigen.

Es besteht jedoch keine Notwendigkeit, Duplikate zu eliminieren, wenn Sie garantieren können, dass die beiden Sätze von Zeilen bereits unterschiedlich sind. Das heißt, wenn Sie garantieren, dass es keine Überschneidungen gibt. Wenn Sie sich darauf verlassen können, wäre es immer ein No-Op, Duplikate zu eliminieren, und daher kann die Abfrage diesen Schritt und damit das kostspielige Sortieren überspringen.

Wenn Sie die Abfragen so ändern, dass garantiert nicht überlappende Teilmengen von Zeilen ausgewählt werden, ist das ein Gewinn.

SELECT * FROM mytable WHERE a=X 
UNION ALL 
SELECT * FROM mytable WHERE b=Y AND a!=X

Diese beiden Sätze haben garantiert keine Überlappung. Wenn der erste Satz Zeilen enthält, in denen a=X und der zweite Satz hat Zeilen mit a!=X dann kann es keine Zeile geben, die in beiden Sätzen enthalten ist.

Die zweite Abfrage fängt daher nur einige ab der Zeilen, in denen b=Y , aber jede Zeile mit a=X AND b=Y ist im ersten Set bereits enthalten.

Damit erreicht die Abfrage eine optimierte Suche nach zwei OR Begriffe, ohne Duplikate zu produzieren, und kein UNION DISTINCT erfordernd Betrieb.