Toujours dans l'optique de rassembler plusieurs requêtes en une seule, voici l'UNION
.
Faire l'union de deux requêtes, cela veut simplement dire réunir les résultats de la première requête et les résultats de la seconde requête.
Voyons comment ça fonctionne !
Syntaxe
La syntaxe d'UNION
est simplissime : vous avez deux requêtes SELECT
dont vous voulez additionner les résultats ; il vous suffit d'ajouter UNION
entre ces deux requêtes.
1 2 3 | SELECT ... UNION SELECT ... |
Le nombre de requêtes qu'il est possible d'unir est illimité. Si vous avez cinquante requêtes de sélection, placez un UNION
entre les cinquante requêtes.
1 2 3 4 5 6 7 8 | SELECT ... UNION SELECT ... UNION SELECT ... .... UNION SELECT ... |
Par exemple, vous pouvez obtenir les chiens et les tortues de la manière suivante :
1 2 3 4 5 6 7 | SELECT Animal.* FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Chat' UNION SELECT Animal.* FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Tortue d''Hermann'; |
Cette requête peut bien sûr être écrite sans UNION
, en faisant tout simplement un seul SELECT
avec WHERE Espece.nom_courant = 'Chat' OR Espece.nom_courant = 'Tortue d''Hermann'
. Il ne s'agit ici que d'un exemple destiné à illustrer la syntaxe. Nous verrons plus loin des exemples de cas où UNION
est indispensable.
Les règles
Bien sûr, ce n'est pas si simple que ça, il faut respecter certaines contraintes.
Nombre de colonnes
Il est absolument indispensable que toutes les requêtes unies renvoient le même nombre de colonnes.
Dans la requête que l'on a écrite ci-dessus, aucun problème puisque l'on sélectionne Animal.* dans les deux requêtes. Mais il ne serait donc pas possible de sélectionner un nombre de colonnes différent dans chaque requête intermédiaire.
Par exemple, la requête ci-dessous renverra une erreur :
1 2 3 4 5 6 7 8 9 10 11 12 | -- Pas le même nombre de colonnes -- ------------------------------------ SELECT Animal.id, Animal.nom, Espece.nom_courant -- 3 colonnes sélectionnées FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Chat' UNION SELECT Animal.id, Animal.nom, Espece.nom_courant, Animal.espece_id -- 4 colonnes sélectionnées FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Tortue d''Hermann'; |
1 | ERROR 1222 (21000): The used SELECT statements have a different number of columns
|
Type et ordre des colonnes
En ce qui concerne le type des colonnes, je pense vous avoir déjà signalé que MySQL est très (vraiment très) permissif. Par conséquent, si vous sélectionnez des colonnes de différents types, vous n'aurez pas d'erreurs, mais vous aurez des résultats un peu… spéciaux.
Prenons la requête suivante :
1 2 3 4 5 6 7 8 9 | SELECT Animal.id, Animal.nom, Espece.nom_courant -- 3e colonne : nom_courant VARCHAR FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Chat' AND Animal.nom LIKE 'C%' UNION SELECT Animal.id, Animal.nom, Espece.id -- 3e colonne : id SMALLINT FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Tortue d''Hermann' AND Animal.nom LIKE 'C%'; |
Vous aurez bien un résultat :
id |
nom |
nom_courant |
---|---|---|
5 |
Choupi |
Chat |
33 |
Caribou |
Chat |
34 |
Capou |
Chat |
37 |
Callune |
Chat |
43 |
Cracotte |
Chat |
44 |
Cawette |
Chat |
50 |
Cheli |
3 |
51 |
Chicaca |
3 |
Mais avouez que ce n'est pas un résultat très cohérent. MySQL va simplement convertir tout en chaînes de caractères pour ne pas avoir de problème. Soyez donc très prudents !
Vous pouvez constater en passant que les noms de colonnes utilisés dans les résultats sont ceux de la première requête effectuée. Vous pouvez bien sûr les renommer avec des alias.
Et enfin, pour l'ordre des colonnes, à nouveau vous n'aurez pas d'erreur tant que vous avez le même nombre de colonnes dans chaque requête, mais vous pouvez avoir des résultats bizarres. En effet, MySQL n'analyse pas les noms des colonnes pour trouver une quelconque correspondance d'une requête à l'autre ; tout se fait sur la base de la position de la colonne dans la requête.
1 2 3 4 5 6 7 | SELECT Animal.id, Animal.nom, Espece.nom_courant FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Chat' AND Animal.nom LIKE 'C%' UNION SELECT Animal.nom, Animal.id, Espece.nom_courant FROM Animal -- 1e et 2e colonnes inversées par rapport à la première requête INNER JOIN Espece ON Animal.espece_id = Espece.id WHERE Espece.nom_courant = 'Tortue d''Hermann' AND Animal.nom LIKE 'C%'; |
id |
nom |
nom_courant |
---|---|---|
5 |
Choupi |
Chat |
33 |
Caribou |
Chat |
34 |
Capou |
Chat |
37 |
Callune |
Chat |
43 |
Cracotte |
Chat |
44 |
Cawette |
Chat |
Cheli |
50 |
Tortue d'Hermann |
Chicaca |
51 |
Tortue d'Hermann |
UNION ALL
Exécutez la requête suivante :
1 2 3 | SELECT * FROM Espece UNION SELECT * FROM Espece; |
Non, non, je ne me suis pas trompée, je vous demande bien d'unir deux requêtes qui sont exactement les mêmes.
Résultat :
id |
nom_courant |
nom_latin |
description |
---|---|---|---|
1 |
Chien |
Canis canis |
Bestiole à quatre pattes qui aime les caresses et tire souvent la langue |
2 |
Chat |
Felis silvestris |
Bestiole à quatre pattes qui saute très haut et grimpe aux arbres |
3 |
Tortue d'Hermann |
Testudo hermanni |
Bestiole avec une carapace très dure |
4 |
Perroquet amazone |
Alipiopsitta xanthops |
Joli oiseau parleur vert et jaune |
Chaque résultat n’apparaît qu'une seule fois. Pour la simple et bonne raison que lorsque vous faites UNION
, les doublons sont effacés. En fait, UNION
est équivalent à UNION DISTINCT
. Si vous voulez conserver les doublons, vous devez utiliser UNION ALL
.
1 2 3 | SELECT * FROM Espece UNION ALL SELECT * FROM Espece; |
id |
nom_courant |
nom_latin |
description |
---|---|---|---|
1 |
Chien |
Canis canis |
Bestiole à quatre pattes qui aime les caresses et tire souvent la langue |
2 |
Chat |
Felis silvestris |
Bestiole à quatre pattes qui saute très haut et grimpe aux arbres |
3 |
Tortue d'Hermann |
Testudo hermanni |
Bestiole avec une carapace très dure |
4 |
Perroquet amazone |
Alipiopsitta xanthops |
Joli oiseau parleur vert et jaune |
1 |
Chien |
Canis canis |
Bestiole à quatre pattes qui aime les caresses et tire souvent la langue |
2 |
Chat |
Felis silvestris |
Bestiole à quatre pattes qui saute très haut et grimpe aux arbres |
3 |
Tortue d'Hermann |
Testudo hermanni |
Bestiole avec une carapace très dure |
4 |
Perroquet amazone |
Alipiopsitta xanthops |
Joli oiseau parleur vert et jaune |
Il n'est pas possible de mélanger UNION DISTINCT
et UNION ALL
dans une même requête. Si vous utilisez les deux, les UNION ALL
seront considérés comme des UNION DISTINCT
.
LIMIT et ORDER BY
LIMIT
Il est possible de restreindre les résultats avec LIMIT
au niveau de la requête entière, ou au niveau des différentes requêtes unies. L'important est que ce soit clair. Par exemple, vous unissez deux requêtes et vous voulez limiter les résultats de la première. La requête suivante est parfaite pour ça :
1 2 3 | SELECT id, nom, 'Race' AS table_origine FROM Race LIMIT 3 UNION SELECT id, nom_latin, 'Espèce' AS table_origine FROM Espece; |
Vous avez bien trois noms de races, suivis de toutes les espèces.
id |
nom |
table_origine |
---|---|---|
1 |
Berger allemand |
Race |
2 |
Berger blanc suisse |
Race |
3 |
Boxer |
Race |
4 |
Alipiopsitta xanthops |
Espèce |
1 |
Canis canis |
Espèce |
2 |
Felis silvestris |
Espèce |
3 |
Testudo hermanni |
Espèce |
En passant, voici un résultat qu'il n'est pas possible d'obtenir sans utiliser UNION
!
Par contre, si vous voulez limiter les résultats de la seconde requête, comment faire ? Essayons la requête suivante.
1 2 3 | SELECT id, nom, 'Race' AS table_origine FROM Race UNION SELECT id, nom_latin, 'Espèce' AS table_origine FROM Espece LIMIT 2; |
id |
nom |
table_origine |
---|---|---|
1 |
Berger allemand |
Race |
2 |
Berger blanc suisse |
Race |
Visiblement, ce n'est pas ce que nous voulions… En fait, LIMIT
a été appliqué à l'ensemble des résultats, après UNION
. Par conséquent, si l'on veut que LIMIT
ne porte que sur la dernière requête, il faut le préciser. Pour ça, il suffit d'utiliser des parenthèses.
1 2 3 | SELECT id, nom, 'Race' AS table_origine FROM Race UNION (SELECT id, nom_latin, 'Espèce' AS table_origine FROM Espece LIMIT 2); |
id |
nom |
table_origine |
---|---|---|
1 |
Berger allemand |
Race |
2 |
Berger blanc suisse |
Race |
3 |
Boxer |
Race |
4 |
Bleu russe |
Race |
5 |
Maine coon |
Race |
6 |
Singapura |
Race |
7 |
Sphynx |
Race |
8 |
Nebelung |
Race |
4 |
Alipiopsitta xanthops |
Espèce |
1 |
Canis canis |
Espèce |
Et voilà le travail !
ORDER BY
Par contre, s'il est possible de trier le résultat final d'une requête avec UNION
, on ne peut pas trier les résultats des requêtes intermédiaires. Par exemple, cette requête trie bien les résultats par ordre anti-alphabétique du nom :
1 2 3 4 | SELECT id, nom, 'Race' AS table_origine FROM Race UNION SELECT id, nom_latin, 'Espèce' AS table_origine FROM Espece ORDER BY nom DESC; |
Il faut bien mettre ici ORDER BY nom
, et surtout pas ORDER BY Race.nom
ou ORDER BY Espece.nom_latin
. En effet, l'ORDER BY
agit sur l'ensemble de la requête, donc en quelque sorte, sur une table intermédiaire composée des résultats des deux requêtes unies. Cette table n'est pas nommée, et ne possède que deux colonnes : id et nom (définies par la première clause SELECT
rencontrée).
id |
nom |
table_origine |
---|---|---|
3 |
Testudo hermanni |
Espèce |
7 |
Sphynx |
Race |
6 |
Singapura |
Race |
8 |
Nebelung |
Race |
5 |
Maine coon |
Race |
2 |
Felis silvestris |
Espèce |
1 |
Canis canis |
Espèce |
3 |
Boxer |
Race |
4 |
Bleu russe |
Race |
2 |
Berger blanc suisse |
Race |
1 |
Berger allemand |
Race |
4 |
Alipiopsitta xanthops |
Espèce |
Vous pouvez bien sûr combiner LIMIT
et ORDER BY
.
1 2 3 4 | (SELECT id, nom, 'Race' AS table_origine FROM Race LIMIT 6) UNION (SELECT id, nom_latin, 'Espèce' AS table_origine FROM Espece LIMIT 3) ORDER BY nom LIMIT 5; |
id |
nom |
table_origine |
---|---|---|
4 |
Alipiopsitta xanthops |
Espèce |
1 |
Berger allemand |
Race |
2 |
Berger blanc suisse |
Race |
4 |
Bleu russe |
Race |
3 |
Boxer |
Race |
Exception pour les tris sur les requêtes intermédiaires
Je vous ai dit qu'il n'était pas possible de trier les résultats d'une requête intermédiaire. En réalité, c'est plus subtil que ça.
Dans une requête intermédiaire, il est possible d'utiliser un ORDER BY
mais uniquement combiné à un LIMIT
. Cela permettra de restreindre les résultats voulus (les X premiers dans l'ordre défini par l'ORDER BY
par exemple).
Prenons la requête suivante :
1 2 3 | (SELECT id, nom, 'Race' AS table_origine FROM Race LIMIT 6) UNION (SELECT id, nom_latin, 'Espèce' AS table_origine FROM Espece LIMIT 3); |
Cela vous renvoie bien 6 races et 3 espèces, mais imaginons que vous ne vouliez pas n'importe quelles races, mais les 6 dernières par ordre alphabétique du nom. Dans ce cas-là, vous pouvez utiliser ORDER BY
en combinaison avec LIMIT
dans la requête intermédiaire :
1 2 3 | (SELECT id, nom, 'Race' AS table_origine FROM Race ORDER BY nom DESC LIMIT 6) UNION (SELECT id, nom_latin, 'Espèce' AS table_origine FROM Espece LIMIT 3); |
id |
nom |
table_origine |
---|---|---|
7 |
Sphynx |
Race |
6 |
Singapura |
Race |
8 |
Nebelung |
Race |
5 |
Maine coon |
Race |
3 |
Boxer |
Race |
4 |
Bleu russe |
Race |
4 |
Alipiopsitta xanthops |
Espèce |
1 |
Canis canis |
Espèce |
2 |
Felis silvestris |
Espèce |
En résumé
- Pour grouper les résultats de deux requêtes
SELECT
, il suffit de placerUNION
entre les deux requêtes. UNION
est équivalent àUNION DISTINCT
. Si l'on ne veut pas éliminer les doublons, il faut utiliserUNION ALL
.- Il est possible de limiter les résultats d'un
UNION
(avecLIMIT
), mais également de limiter les résultats de chaque requête composant l'UNION
. - Par contre,
ORDER BY
n'aura d'influence que s'il est utilisé sur le résultat final d'unUNION
.