Utilisation de l'union et de la clause order by dans mysql

123

Je veux utiliser order by with union dans la requête mysql. Je récupère différents types d'enregistrement en fonction de différents critères dans une table basée sur la distance pour une recherche sur mon site. La première requête de sélection renvoie des données liées à la recherche de lieu exacte. La deuxième requête de sélection renvoie des données relatives à la distance à moins de 5 km du lieu recherché. La troisième requête de sélection renvoie des données relatives à la distance dans un rayon de 5 à 15 km du lieu recherché.

Ensuite, j'utilise l'union pour fusionner tous les résultats et les afficher sur une page avec pagination. Sous la rubrique appropriée comme «Résultats de recherche exacts» , «Résultats dans un rayon de 5 km», etc.

Maintenant, je veux trier les résultats en fonction de l'identifiant ou de la date d'ajout. Mais quand j'ajoute ordre par clause à la fin de ma requête (query1 union query 2 union query 3 order by add_date). Il trie tous les résultats. Mais ce que je veux, c'est qu'il devrait trier sous chaque rubrique.

Aditya
la source
Quel (s) type (s) est / sont le (s) champ (s) que vous souhaitez trier dans chaque table?
user151841

Réponses:

221

Vous pouvez le faire en ajoutant une pseudo-colonne nommée rank à chaque sélection, que vous pouvez trier d'abord, avant de trier selon vos autres critères, par exemple:

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
la source
9
Pourquoi il y a aet la fin?
Uday Hiwarale
25
MySQL exige que les tables dérivées aient un alias.
RedFilter
4
@RedFilter, je ne comprends pas tout à fait. Quel est l'intérêt de "sélectionner tout comme une table dérivée" quand nous pouvons simplement sélectionner les 3 tables puis faire un order by? ( "pour trier ou limiter le UNIONrésultat entier , SELECTmettez entre parenthèses les instructions individuelles et placez le ORDER BYou LIMITaprès le dernier" ). Comparez votre code avec i.stack.imgur.com/LpTMU.png
Pacerier
1
@Pacerier Si cette syntaxe fonctionne avec MySQL, foncez! Ma syntaxe est un peu plus multiplateforme, je ne travaille généralement pas avec MySQL. Je préfère en fait cela, cependant, car cela fonctionne dans des cas plus généralisés, par exemple, considérez quand vous ne voulez que les dix premières correspondances de la dernière requête UNION - je pense que vous devriez de toute façon revenir à ma syntaxe.
RedFilter
1
@Pacerier, considérez également que la valeur de cette manière permet à ce top "select *" d'être à la place "select id, add_date" et de supprimer "rank" des résultats si vous ne vouliez pas les voir.
Dev Null
45

Vous pouvez utiliser des sous-requêtes pour ce faire:

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
la source
1
Tnx, bon pour séparer les commandes. Nous pouvons également avoir un arrangement différent si la clause de commande était commune mais nous voulons que les résultats soient séparés (au lieu de séparer les commandes): 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
Ceci est incorrect : l' utilisation de ORDER BYpour des SELECTdéclarations individuelles n'implique rien sur l'ordre dans lequel les lignes apparaissent dans le résultat final
shmosel
Vous avez raison, la réponse acceptée est également la bonne. Cela a bien fonctionné lorsque je l'ai testé.
rickythefox
shmosel (et rickythefox) - union all ne supprime pas les doublons, et n'a donc aucune raison de changer le type de vos sélections triées individuelles. C'est pourquoi cela a fonctionné pour vous dans le passé et pourquoi cela fonctionnera pour vous à l'avenir. L'ordre perturbé dans le résultat final est dû au fait que UNION (DISTINCT) a besoin d'une sorte de tri pour combiner efficacement les lignes. Ou plus précisément, trie car il combine efficacement les lignes. Vous pouvez donc compter sur l'union pour tous conserver vos sous-requêtes.
Gerard ONeill
2
Cela fonctionnait très bien pour les anciennes versions de MySQL, maintenant cette approche ne fonctionnera PAS . Parce que la norme SQL ne garantit pas de conserver l'ordre des sous-requêtes.
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
la source
Cela a fonctionné, mais bizarrement, j'avais besoin de changer une chose: je devais donner un alias partagé au champ qui passait la commande. A travaillé en plus, merci!
HoldOffHunger
9

Une requête Union ne peut avoir qu'une seule ORDER BYclause maîtresse , IIRC. Pour obtenir cela, dans chaque requête constituant la UNIONrequête la plus importante , ajoutez un champ qui sera celui que vous triez pour les UNION's ORDER BY.

Par exemple, vous pourriez avoir quelque chose comme

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

Ce union_sortchamp peut être tout ce que vous souhaitez trier. Dans cet exemple, il arrive juste de placer les résultats de la première table en premier, de la deuxième table en second, etc.

user151841
la source
9

N'oubliez pas, union all est un moyen d'ajouter des enregistrements à un jeu d'enregistrements sans trier ni fusionner (par opposition à l'union).

Donc par exemple:

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

Il maintient les requêtes individuelles plus claires et vous permet de trier par différents paramètres dans chaque requête. Cependant, en utilisant la manière de la réponse sélectionnée, cela peut devenir plus clair en fonction de la complexité et de la relation entre les données, car vous conceptualisez le tri. Il vous permet également de renvoyer la colonne artificielle au programme d'interrogation afin qu'elle dispose d'un contexte dans lequel il peut trier ou organiser.

Mais cette méthode a l'avantage d'être rapide, de ne pas introduire de variables supplémentaires et de faciliter la séparation de chaque requête, y compris le tri. La possibilité d'ajouter une limite est simplement un bonus supplémentaire.

Et bien sûr, n'hésitez pas à transformer le syndicat en une union et à ajouter un tri pour l'ensemble de la requête. Ou ajoutez un identifiant artificiel, auquel cas cette méthode permet de trier facilement selon différents paramètres dans chaque requête, mais sinon, c'est la même chose que la réponse acceptée.

Gérard ONeill
la source
4

J'ai fait travailler cela sur une union plus syndicale.

(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
la source
3

Lorsque vous utilisez une ORDER BYclause à l'intérieur d'une sous-requête utilisée en conjonction avec un UNIONmysql, la ORDER BYclause sera optimisée .

En effet, par défaut, a UNIONrenvoie une liste non ordonnée, donc an ORDER BYne ferait rien.

L'optimisation est mentionnée dans la documentation et dit:

Pour appliquer ORDER BY ou LIMIT à un SELECT individuel, placez la clause à l'intérieur des parenthèses qui entourent le SELECT:

(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);

Cependant, l'utilisation de ORDER BY pour des instructions SELECT individuelles n'implique rien sur l'ordre dans lequel les lignes apparaissent dans le résultat final car UNION produit par défaut un ensemble de lignes non ordonné. Par conséquent, l'utilisation de ORDER BY dans ce contexte est généralement associée à LIMIT, de sorte qu'il est utilisé pour déterminer le sous-ensemble des lignes sélectionnées à récupérer pour le SELECT, même si cela n'affecte pas nécessairement l'ordre de ces lignes dans le résultat final UNION. Si ORDER BY apparaît sans LIMIT dans un SELECT, il est optimisé car il n'aura aucun effet de toute façon.

La dernière phrase est un peu trompeuse car elle devrait avoir un effet. Cette optimisation pose un problème lorsque vous vous trouvez dans une situation où vous devez commander dans la sous-requête.

Pour forcer MySQL à ne pas faire cette optimisation, vous pouvez ajouter une clause LIMIT comme ceci:

(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)

Un high LIMITsignifie que vous pouvez ajouter un OFFSETsur la requête globale si vous voulez faire quelque chose comme la pagination.

Cela vous donne également l'avantage supplémentaire de pouvoir ORDER BYutiliser différentes colonnes pour chaque union.

hdifen
la source
0

Essayer:

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

Où [QUERY 1] et [QUERY 2] sont vos deux requêtes que vous souhaitez fusionner.

André Hoffmann
la source
0

C'est parce que vous triez le jeu de résultats entier, vous devez trier, chaque partie de l'union séparément, ou vous pouvez utiliser ORDER BY (Quelque chose c'est-à-dire. Distance de sous-requête) THEN (quelque chose comme id de ligne) clause

canni
la source
0

J'ai essayé d'ajouter l'ordre par à chacune des requêtes avant l'union comme

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

mais cela ne semblait pas fonctionner. Il n'a pas fait le classement dans les lignes de chaque sélection.

Je pense que vous devrez garder l'ordre à l'extérieur et ajouter les colonnes de la clause where à l'ordre par, quelque chose comme

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

Cela peut être un peu délicat, car vous souhaitez regrouper par plages, mais je pense que cela devrait être faisable.

Mike C
la source
user151841 L'idée union_sort ci-dessous serait un bon moyen de gérer le regroupement
Mike C