SQL : Casse-tête d'un tri sur une requête employant UNION ALL

Nouveau WRInaute
Bonjour à la communauté WRI.
Je me heurte à un souci en matière de tri d'un résultat sql.

Description : Soit 1 table UTILISATEUR composée de 3 champs :
  1. Champ Nom_utilisateur de type texte
  2. Champ Pays_utilisateur de type texte
  3. Champ Partenaire (1 pour partenaire, 0 pour non-partenaire)

J'aimerais projeter une liste des utilisateurs dans l'ordre suivant :
- d'abord les utilisateurs français qui sont partenaires triés par ordre alphabétique
- Puis tous les utilisateurs français (partenaires compris) classés par ordre alphabétique.
[Note : les partenaires seront donc affichés deux fois dans la liste finale].

J'avais pensé à :
(SELECT Nom_utilisateur, Pays_utilisateur
FROM UTILISATEUR
WHERE partenaire="1"
AND pays IN ("France")
ORDER BY Nom_utilisateur ASC)
UNION ALL
(SELECT Nom_utilisateur, Pays_utilisateur
FROM UTILISATEUR
WHERE pays IN ("France")
ORDER BY Nom_utilisateur ASC) ;

Les enregistrements affichés sont les bons et j'ai bien un affichage
1 - des partenaires français
2 - puis utilisateurs français... mais à l'intérieur de ces deux groupes le tri est incohérent.
:arrow: Le Order By visiblement ne passe pas.

Je sollicite quelques techniciens du SQL pour m'aider à y voir plus clair et aboutir à un tri alphabétique à l'intérieur de ces deux groupes...
Merci !
 
Nouveau WRInaute
Si il s'agit de tester le tri décroissant sur le champ Nom_utilisateur : oui, j'ai testé, le tri à l'intérieur de ces 2 groupes est tout aussi incohérent. :|
 
WRInaute accro
C'est même un hasard que les deux groupes soient l'un après l'autre, UNION ne donne aucune garantie que l'ordre sera conservé, et les clauses ORDER à l'intérieur sont tout bonnement ignorées (sauf s'il y a un LIMIT avec par exemple).

La solution est simple:
SELECT champs FROM (SELECT 1 AS critere,champs FROM table WHERE conditions UNION ALL SELECT 2,champs FROM table WHERE conditions) x ORDER BY critere,autres_criteres_de_tri

Jacques.
 
WRInaute accro
NB: je parie une fraise tagada que tout ça est d'ailleurs expliqué dans la doc avec exemples à l'appui. Bref, RTFM :-)

Jacques.
 
Nouveau WRInaute
jcaron a dit:
NB: je parie une fraise tagada que tout ça est d'ailleurs expliqué dans la doc avec exemples à l'appui. Bref, RTFM :-)
Jacques.
Merci Jacques,
tu sais, il ne faut pas m'en vouloir, je code comme une laitue.
jcaron a dit:
La solution est simple:
SELECT champs FROM (SELECT 1 AS critere,champs FROM table WHERE conditions UNION ALL SELECT 2,champs FROM table WHERE conditions) x ORDER BY critere,autres_criteres_de_tri
Jacques.
Si je te suis, j'aboutirais à la requête suivante :
SELECT Nom_utilisateur, Pays_utilisateur
FROM (SELECT 1 AS critere, Nom_utilisateur, Pays_utilisateur
FROM UTILISATEUR
WHERE partenaire="1" AND pays IN ("France")
UNION ALL
SELECT 2, Nom_utilisateur, Pays_utilisateur
FROM UTILISATEUR
WHERE pays IN ("France") )
x ORDER BY critere, Nom_utilisateur ;
Je vais tester. Le x avant Order By, c'est pour moi extra-terrestre, je te donnerai un retour ce soir.
Merci moulte fois, Jacques.
RTFM, c'est une radio de Saint-Domingue, Hein ? :)
 
WRInaute accro
Tu as oublié as critere après le select 2 et à mon avis tu n'as pas besoin de faire union all il suffit de faire union select
(et puis je ne comprends pas pourquoi tu fais pays IN ("France") et pas pays = "France" ?)
 
Nouveau WRInaute
C'est noté pour As critere.
Je te donnerai la réponse concernant union all et union select... mais à mon avis, union all permet les doublons que union select éliminera.
Pour le critère de restriction IN ("France") : j'ai simplifié volontairement le contexte et la requête qui porte en réalité sur une trentaine de pays.
Merci pour ton intervention, Marie-Aude.
 
Nouveau WRInaute
Merci Jacques et Marie-Aude,
Bilan : Union all n'est pas nécessaire ici, union suffit. Bien vu, Marie-Aude. :)
J'ai compris a quoi servait l'utilité de mettre un alias après une requête imbriquée. Merci Jacques.
Where'sMyFM ? :)
 
WRInaute accro
Je pense que le "AS critere" dans le deuxième select n'est pas nécessaire: dans un UNION, les noms des champs viennent du premier select, ceux des suivants sont ignorés, il faut juste que le nombre et le type des champs (et leur ordre évidemment) soient les mêmes.

Le "ALL" du UNION n'est pas nécessaire ici parce que le critere (1,2) est différent. Ceci dit, pour la lisibilité je pense que c'est mieux, et pour la performance aussi (un UNION sans ALL oblige le serveur à vérifier s'il y a des doublons, donc ça lui prend plus de temps que de coller tout bêtement à la suite).

Jacques.

PS: le problème n'est pas de coder comme une dinde, c'est juste que tu gagneras beaucoup de temps si tu as le réflexe d'aller lire la doc plutôt que de tourner en rond autour de la question et/ou d'aller poser la question dans un forum. Et en général, en plus de trouver la réponse à ta question, tu vas aussi y apprendre quelques autres trucs au passage, qui serviront bien un jour ou un autre...
 
WRInaute discret
Je déterre un vieux sujet, mais je galère vraiment sur un cas similaire.

Je comprends pas vraiment pourquoi le résultat donné dans l'exemple marche. Car selon moi le order se fait sur la requête final et mélange du coup les deux sélect, alors que au départ il était question d'afficher d'abord les résultat du premier sélect et ensuite les résultat du deuxième sélect qui eux même étaient déjà trier grasse à un order.

Voilà mon cas, j'espère que quelqu'un pourra m'aider :

$query_global = mysql_query("(SELECT SQL_CALC_FOUND_ROWS id FROM matable
WHERE titre='test' ORDER by ref ASC)
UNION (SELECT id FROM matable
WHERE description='test' ORDER by ref ASC)
LIMIT ".$premierMessageAafficher.", ".$nombreDeMessagesParPage) or die (mysql_error());

En sachant que je souhaite d'abord faire ressortir les résultat là ou test est dans titre (classé par ref) et ensuite les résultat là ou test est dans description (classé par ref)

merci de votre aide

Clara
 
WRInaute accro
La réponse (avec l'explication du pourquoi et du comment) est dans le 4e message de la discussion.

Jacques.
 
WRInaute discret
Merci de ta réponse. J'ai lu et relu le message et franchement je ne comprends pas. J'ai d'ailleurs testé le code et les résultats ne sont pas affichés d'abord avec le premier sélect et ensuite le deuxième.

Tous est affiché en même temps.
 
WRInaute accro
Quel code tu as testé? Parce que le code que tu as donné ne correspond pas du tout à ce qui est expliqué.

Le principe, c'est que:
- le ORDER BY dans les différents SELECT qui sont agrégés ne sert à rien, sauf si tu utilises un LIMIT dans le select en question
- tu ajoutes un champ à chaque SELECT qui correspond à l'ordre de tri des différentes parties (1 pour le premier SELECT, 2 pour le deuxième, etc.)
- tu mets ton UNION dans une sous-requête
- tu appliques un ORDER BY sur le SELECT global, en triant en premier par le champ ajouté, puis par les autres champs utiles

Jacques.
 
Discussions similaires
Haut