Verwenden von union und order by Klausel in MySQL

123

Ich möchte order by with union in der MySQL-Abfrage verwenden. Ich rufe verschiedene Arten von Datensätzen basierend auf verschiedenen Kriterien aus einer Tabelle basierend auf der Entfernung für eine Suche auf meiner Website ab. Die erste Auswahlabfrage gibt Daten zurück, die sich auf die genaue Ortssuche beziehen. Die zweite Auswahlabfrage gibt Daten zurück, die sich auf die Entfernung innerhalb von 5 km vom gesuchten Ort beziehen. Die dritte Auswahlabfrage gibt Daten zurück, die sich auf die Entfernung innerhalb von 5 bis 15 km vom gesuchten Ort beziehen.

Dann verwende ich Union, um alle Ergebnisse zusammenzuführen und auf einer Seite mit Paging anzuzeigen. Unter der entsprechenden Überschrift "Genaue Suchergebnisse" , "Ergebnisse innerhalb von 5 km" usw.

Jetzt möchte ich die Ergebnisse nach id oder add_date sortieren. Aber wenn ich am Ende meiner Abfrage order by-Klausel hinzufüge (query1 union query 2 union query 3 order by add_date). Es sortiert alle Ergebnisse. Aber was ich will ist, dass es unter jeder Überschrift sortieren sollte.

Aditya
quelle
Welche Art (en) sind / sind die Felder, nach denen Sie in jeder Tabelle sortieren möchten?
user151841

Antworten:

221

Sie können dies tun, indem Sie jeder Auswahl eine Pseudospalte mit dem Namen rank hinzufügen, nach der Sie zuerst sortieren können, bevor Sie nach Ihren anderen Kriterien sortieren, z.

select *
from (
    select 1 as Rank, id, add_date from Table 
    union all
    select 2 as Rank, id, add_date from Table where distance < 5
    union all
    select 3 as Rank, id, add_date from Table where distance between 5 and 15
) a
order by rank, id, add_date desc
RedFilter
quelle
9
Warum gibt es aund das Ende?
Uday Hiwarale
25
MySQL erfordert, dass abgeleitete Tabellen einen Alias ​​haben.
RedFilter
4
@ RedFilter, ich verstehe es nicht ganz. Was ist der springende Punkt bei "Alle als abgeleitete Tabelle auswählen ", wenn wir einfach alle 3 Tabellen auswählen und dann eine order byausführen können? ( "Um das gesamte UNIONErgebnis zu sortieren oder einzuschränken , setzen Sie die einzelnen SELECTAnweisungen in Klammern und setzen Sie das ORDER BYoder LIMITnach dem letzten" ). Vergleichen Sie Ihren Code mit i.stack.imgur.com/LpTMU.png
Pacerier
1
@Pacerier Wenn diese Syntax mit MySQL funktioniert, machen Sie es! Meine Syntax ist etwas plattformübergreifender, ich arbeite im Allgemeinen nicht mit MySQL. Ich bevorzuge es jedoch, da es in allgemeineren Fällen funktioniert, z. B. wenn Sie nur die ersten zehn Übereinstimmungen aus der letzten UNION-Abfrage möchten - ich denke, Sie müssten sowieso auf meine Syntax zurückgreifen.
RedFilter
1
@Pacerier, denken Sie auch daran, dass der Wert auf diese Weise ermöglicht, dass das oberste "select *" stattdessen "select id, add_date" ist und "rank" aus den Ergebnissen entfernt, wenn Sie sie nicht sehen möchten.
Dev Null
45

Sie können dazu Unterabfragen verwenden:

select * from (select values1 from table1 order by orderby1) as a
union all
select * from (select values2 from table2 order by orderby2) as b
Rickythefox
quelle
1
Tnx, gut, um Bestellungen zu trennen. Wir können auch eine andere Anordnung treffen, wenn die Bestellklausel gemeinsam war, aber wir möchten, dass die Ergebnisse getrennt werden (anstatt Aufträge zu trennen): SELECT * FROM ( SELECT aaa AS Col, 1 AS Official FROM table1 UNION ALL SELECT bbb AS Col, 0 AS Official FROM table2 ) AS tbl ORDER BY Official, Col
Alix
18
Dies ist falsch : Die Verwendung von ORDER BYfür einzelne SELECTAnweisungen impliziert nichts über die Reihenfolge, in der die Zeilen im Endergebnis erscheinen
shmosel
Sie haben Recht, die akzeptierte Antwort ist auch die richtige. Das hat einfach gut funktioniert, als ich es getestet habe.
Rickythefox
shmosel (und rickythefox) - union all entfernt keine Duplikate und hat daher keinen Grund, die Art Ihrer individuell sortierten Auswahl zu ändern. Dies ist der Grund, warum es in der Vergangenheit für Sie funktioniert hat und warum es in Zukunft für Sie funktionieren wird. Die Reihenfolge, die im Endergebnis durcheinander gebracht wird, ist darauf zurückzuführen, dass UNION (DISTINCT) eine Art Sortierung benötigt, um Zeilen effizient zu kombinieren. Oder genauer gesagt, sortiert, weil es Zeilen effizient kombiniert. Sie können sich also darauf verlassen, dass alle Ihre Unterabfragen erhalten.
Gerard ONeill
2
Dies funktionierte hervorragend für ältere MySQL-Versionen, jetzt funktioniert dieser Ansatz NICHT mehr . Da der SQL-Standard nicht garantiert, dass die Reihenfolge der Unterabfragen beibehalten wird.
Andrei
28
(select add_date,col2 from table_name) 
  union 
(select add_date,col2 from table_name) 
  union 
(select add_date,col2 from table_name) 

order by add_date
Mitali
quelle
Das hat funktioniert, aber seltsamerweise musste ich eines ändern: Ich musste dem Feld, nach dem bestellt wurde, einen gemeinsamen Alias ​​geben. Arbeitete außerdem, danke!
HoldOffHunger
9

Eine Union-Abfrage kann nur eine Master- ORDER BYKlausel haben, IIRC. Um dies zu erhalten UNION, fügen Sie in jeder Abfrage, aus der die größere Abfrage besteht, ein Feld hinzu, nach dem Sie für die UNION's sortieren ORDER BY.

Zum Beispiel könnten Sie so etwas haben

SELECT field1, field2, '1' AS union_sort
UNION SELECT field1, field2, '2' AS union_sort
UNION SELECT field1, field2, '3' AS union_sort
ORDER BY union_sort

Dieses union_sortFeld kann alles sein, nach dem Sie sortieren möchten. In diesem Beispiel werden nur die Ergebnisse der ersten Tabelle an erster Stelle, der zweiten Tabelle an zweiter Stelle usw. gesetzt.

user151841
quelle
9

Vergessen Sie nicht, dass union all eine Möglichkeit ist, Datensätze zu einem Datensatz hinzuzufügen, ohne sie zu sortieren oder zusammenzuführen (im Gegensatz zu union).

Also zum Beispiel:

select * from (
    select col1, col2
    from table a
    <....>
    order by col3
    limit by 200
) a
union all
select * from (
    select cola, colb
    from table b
    <....>
    order by colb
    limit by 300
) b

Dadurch werden die einzelnen Abfragen übersichtlicher und Sie können in jeder Abfrage nach verschiedenen Parametern sortieren. Wenn Sie jedoch die Art der ausgewählten Antwort verwenden, wird dies je nach Komplexität und Beziehung zwischen den Daten möglicherweise klarer, da Sie die Sortierung konzipieren. Außerdem können Sie die künstliche Spalte an das Abfrageprogramm zurückgeben, damit sie einen Kontext hat, nach dem sie sortieren oder organisieren kann.

Diese Methode hat jedoch den Vorteil, dass sie schnell ist, keine zusätzlichen Variablen einführt und es einfach macht, jede Abfrage einschließlich der Sortierung zu trennen. Die Möglichkeit, ein Limit hinzuzufügen, ist einfach ein zusätzlicher Bonus.

Und natürlich können Sie die Union in eine Union verwandeln und eine Sortierung für die gesamte Abfrage hinzufügen. Oder fügen Sie eine künstliche ID hinzu. In diesem Fall ist es auf diese Weise einfach, in jeder Abfrage nach verschiedenen Parametern zu sortieren. Andernfalls entspricht sie der akzeptierten Antwort.

Gerard ONeill
quelle
4

Ich habe das an einer Join Plus Union arbeiten lassen.

(SELECT 
   table1.column1,
   table1.column2,
   foo1.column4
 FROM table1, table2, foo1, table5
 WHERE table5.somerecord = table1.column1
 ORDER BY table1.column1 ASC, table1.column2 DESC
)

UNION

(SELECT
    ... Another complex query as above
)

ORDER BY column1 DESC, column2 ASC
Robert Saylor
quelle
3

Wenn Sie eine ORDER BYKlausel in einer Unterabfrage verwenden, die in Verbindung mit einer UNIONMySQL verwendet wird, wird die ORDER BYKlausel optimiert .

Dies liegt daran, dass a standardmäßig eine UNIONungeordnete Liste zurückgibt, sodass a ORDER BYnichts tun würde.

Die Optimierung wird in den Dokumenten erwähnt und sagt:

Um ORDER BY oder LIMIT auf eine einzelne SELECT anzuwenden, setzen Sie die Klausel in die Klammern, die die SELECT einschließen:

(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10) UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);

Die Verwendung von ORDER BY für einzelne SELECT-Anweisungen impliziert jedoch nichts über die Reihenfolge, in der die Zeilen im Endergebnis angezeigt werden, da UNION standardmäßig eine ungeordnete Reihe von Zeilen erzeugt. Daher wird die Verwendung von ORDER BY in diesem Kontext normalerweise in Verbindung mit LIMIT verwendet, sodass die Teilmenge der ausgewählten Zeilen ermittelt wird, die für SELECT abgerufen werden sollen, obwohl dies nicht unbedingt die Reihenfolge dieser Zeilen in der Liste beeinflusst endgültiges UNION-Ergebnis. Wenn ORDER BY ohne LIMIT in einem SELECT angezeigt wird, wird es wegoptimiert, da es ohnehin keine Auswirkung hat.

Der letzte Satz ist etwas irreführend, weil er eine Wirkung haben sollte. Diese Optimierung verursacht ein Problem, wenn Sie sich in einer Situation befinden, in der Sie innerhalb der Unterabfrage bestellen müssen.

Um MySQL zu zwingen, diese Optimierung nicht durchzuführen, können Sie eine LIMIT-Klausel wie folgt hinzufügen:

(SELECT 1 AS rank, id, add_date FROM my_table WHERE distance < 5 ORDER BY add_date LIMIT 9999999999)
UNION ALL
(SELECT 2 AS rank, id, add_date FROM my_table WHERE distance BETWEEN 5 AND 15 ORDER BY rank LIMIT 9999999999)
UNION ALL
(SELECT 3 AS rank, id, add_date from my_table WHERE distance BETWEEN 5 and 15 ORDER BY id LIMIT 9999999999)

Ein High LIMITbedeutet, dass Sie OFFSETder Gesamtabfrage eine hinzufügen können, wenn Sie beispielsweise eine Paginierung durchführen möchten.

Dies gibt Ihnen auch den zusätzlichen Vorteil, dass Sie ORDER BYfür jede Union unterschiedliche Spalten verwenden können.

hdifen
quelle
0

Versuchen:

SELECT result.* 
FROM (
 [QUERY 1]
 UNION
 [QUERY 2]
) result
ORDER BY result.id

Wobei [ABFRAGE 1] und [ABFRAGE 2] Ihre beiden Abfragen sind, die Sie zusammenführen möchten.

André Hoffmann
quelle
0

Dies liegt daran, dass Sie die gesamte Ergebnismenge sortieren, jeden Teil der Vereinigung separat sortieren sollten oder die Klausel ORDER BY (etwas, dh Unterabfragedistanz) THEN (etwas, dh Zeilen-ID) verwenden können

Canni
quelle
0

Ich habe versucht, die Reihenfolge nach zu jeder der Abfragen hinzuzufügen, bevor ich wie gewerkschaftlich organisiert wurde

(select * from table where distance=0 order by add_date) 
union 
(select * from table where distance>0 and distance<=5 order by add_date)

aber es schien nicht zu funktionieren. Die Reihenfolge innerhalb der Zeilen aus jeder Auswahl wurde nicht ausgeführt.

Ich denke, Sie müssen die Reihenfolge von außen beibehalten und die Spalten in der where-Klausel zur Bestellung von hinzufügen, so etwas wie

(select * from table where distance=0) 
union 
(select * from table where distance>0 and distance<=5) 
order by distance, add_date

Dies mag etwas schwierig sein, da Sie nach Bereichen gruppieren möchten, aber ich denke, es sollte machbar sein.

Mike C.
quelle
user151841 union_sort Idee unten wäre ein guter Weg, um die Gruppierung zu handhaben
Mike C