Mysql
 sql >> Database >  >> RDS >> Mysql

Come usare ORDER BY all'interno di UNION

Qualcosa del genere dovrebbe funzionare in MySQL:

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

per restituire le righe nell'ordine in cui vorremmo che fossero restituite. cioè MySQL sembra onorare il ORDER BY clausole all'interno delle viste inline.

Ma senza un ORDER BY clausola sulla query più esterna, l'ordine di restituzione delle righe è non garantito.

Se abbiamo bisogno delle righe restituite in una sequenza particolare, possiamo includere un ORDER BY sulla query più esterna. In molti casi d'uso, possiamo semplicemente usare un ORDER BY sulla query più esterna per soddisfare i risultati.

Ma quando abbiamo un caso d'uso in cui abbiamo bisogno di tutte le righe della prima query restituite prima di tutte le righe della seconda query, un'opzione consiste nell'includere una colonna discriminante aggiuntiva in ciascuna delle query. Ad esempio, aggiungi ,'a' AS src nella prima query, ,'b' AS src alla seconda query.

Quindi la query più esterna potrebbe includere ORDER BY src, name , per garantire la sequenza dei risultati.

SEGUITO

Nella tua query originale, il ORDER BY nelle tue query viene scartato dall'ottimizzatore; poiché non esiste ORDER BY applicato alla query esterna, MySQL è libero di restituire le righe nell'ordine che preferisce.

Il "trucco" nella query nella mia risposta (sopra) dipende dal comportamento che potrebbe essere specifico di alcune versioni di MySQL.

Caso di prova:

popolare tabelle

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

interrogare

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

set di risultati restituito

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

Le righe da foo2 vengono restituiti "in ordine", seguiti dalle righe di foo3 , ancora, "in ordine".

Nota (di nuovo) che questo comportamento NON garantita. (Il comportamento che osserviamo è un effetto collaterale del modo in cui MySQL elabora le viste inline (tabelle derivate). Questo comportamento potrebbe essere diverso nelle versioni successive alla 5.5.)

Se hai bisogno delle righe restituite in un ordine particolare, specifica un ORDER BY clausola per la query più esterna. E quell'ordine si applicherà a intero set di risultati.

Come accennato in precedenza, se avessi bisogno delle righe della prima query prima, seguite dalla seconda query, includerei una colonna "discriminatore" in ciascuna query e quindi includerei la colonna "discriminatore" nella clausola ORDER BY. Vorrei anche eliminare le viste in linea e fare qualcosa del genere:

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