Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment utiliser ORDER BY dans UNION

Quelque chose comme ceci devrait fonctionner dans MySQL :

SELECT a.*
  FROM ( 
         SELECT ...  FROM ... ORDER BY ... 
       ) a
 UNION ALL 
SELECT b.*
  FROM ( 
         SELECT ...  FROM ... ORDER BY ... 
       ) b

pour renvoyer les lignes dans un ordre dans lequel nous aimerions qu'elles soient renvoyées. c'est-à-dire que MySQL semble respecter le ORDER BY clauses à l'intérieur des vues en ligne.

Mais, sans ORDER BY clause sur la requête la plus externe, l'ordre dans lequel les lignes sont renvoyées n'est pas garanti.

Si nous avons besoin que les lignes soient renvoyées dans une séquence particulière, nous pouvons inclure un ORDER BY sur la requête la plus externe. Dans de nombreux cas d'utilisation, nous pouvons simplement utiliser un ORDER BY sur la requête la plus externe pour satisfaire les résultats.

Mais lorsque nous avons un cas d'utilisation où nous avons besoin que toutes les lignes de la première requête soient renvoyées avant toutes les lignes de la deuxième requête, une option consiste à inclure une colonne de discriminateur supplémentaire dans chacune des requêtes. Par exemple, ajoutez ,'a' AS src dans la première requête, ,'b' AS src à la deuxième requête.

Ensuite, la requête la plus externe pourrait inclure ORDER BY src, name , pour garantir l'enchaînement des résultats.

SUIVI

Dans votre requête d'origine, le ORDER BY dans vos requêtes est ignoré par l'optimiseur ; puisqu'il n'y a pas de ORDER BY appliqué à la requête externe, MySQL est libre de renvoyer les lignes dans l'ordre qu'il souhaite.

Le "truc" de la requête dans ma réponse (ci-dessus) dépend du comportement qui peut être spécifique à certaines versions de MySQL.

Scénario de test :

remplir les tableaux

CREATE TABLE foo2 (id INT PRIMARY KEY, role VARCHAR(20)) ENGINE=InnoDB;
CREATE TABLE foo3 (id INT PRIMARY KEY, role VARCHAR(20)) ENGINE=InnoDB;

INSERT INTO foo2 (id, role) VALUES 
  (1,'sam'),(2,'frodo'),(3,'aragorn'),(4,'pippin'),(5,'gandalf');
INSERT INTO foo3 (id, role) VALUES 
  (1,'gimli'),(2,'boromir'),(3,'elron'),(4,'merry'),(5,'legolas');

requête

SELECT a.*
  FROM ( SELECT s.id, s.role
           FROM foo2 s
          ORDER BY s.role
       ) a
 UNION ALL
SELECT b.*
  FROM ( SELECT t.id, t.role
           FROM foo3 t
          ORDER BY t.role
       ) b

jeu de résultats renvoyé

    id  role     
 ------  ---------
      3  aragorn  
      2  frodo    
      5  gandalf  
      4  pippin   
      1  sam      
      2  boromir  
      3  elron    
      1  gimli    
      5  legolas  
      4  merry    

Les lignes de foo2 sont renvoyés "dans l'ordre", suivis des lignes de foo3 , encore une fois, "dans l'ordre".

Notez (encore) que ce comportement n'est PAS garanti. (Le comportement que nous observons est un effet secondaire de la façon dont MySQL traite les vues en ligne (tables dérivées). Ce comportement peut être différent dans les versions postérieures à la 5.5.)

Si vous avez besoin que les lignes soient renvoyées dans un ordre particulier, spécifiez un ORDER BY clause pour la requête la plus externe. Et cet ordre s'appliquera à l'intégralité jeu de résultats.

Comme je l'ai mentionné précédemment, si j'avais d'abord besoin des lignes de la première requête, suivies de la deuxième requête, j'inclurais une colonne "discriminateur" dans chaque requête, puis j'inclurais la colonne "discriminateur" dans la clause ORDER BY. Je supprimerais également les vues en ligne et ferais quelque chose comme ceci :

SELECT s.id, s.role, 's' AS src
  FROM foo2 s
 UNION ALL
SELECT t.id, t.role, 't' AS src
  FROM foo3 t
 ORDER BY src, role