Usando la clausola union e order by in mysql


123

Voglio usare order by con union nella query mysql. Sto recuperando diversi tipi di record in base a criteri diversi da una tabella basata sulla distanza per una ricerca sul mio sito. La prima query di selezione restituisce i dati relativi alla ricerca del luogo esatto. La seconda query di selezione restituisce i dati relativi alla distanza entro 5 km dal luogo cercato. La terza query di selezione restituisce i dati relativi alla distanza entro 5-15 km dal luogo cercato.

Quindi sto usando union per unire tutti i risultati e mostrarli su una pagina con paging. Sotto l'intestazione appropriata come "Risultati di ricerca esatti" , "Risultati entro 5 km" ecc

Ora voglio ordinare i risultati in base a id o add_date. Ma quando aggiungo la clausola order by alla fine della mia query (query1 union query 2 union query 3 order by add_date). Ordina tutti i risultati. Ma quello che voglio è che dovrebbe ordinare sotto ogni intestazione.


Di che tipo sono i campi in base ai quali si desidera ordinare in ciascuna tabella?
user151841

Risposte:


221

Puoi farlo aggiungendo una pseudo-colonna denominata rango a ciascuna selezione, che puoi ordinare per prima, prima di ordinare in base agli altri criteri, ad esempio:

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

9
Perché c'è ae la fine?
Uday Hiwarale

25
MySQL richiede che le tabelle derivate abbiano un alias.
RedFilter

4
@RedFilter, non lo capisco. Qual è lo scopo di "seleziona tutto come tabella derivata" quando possiamo semplicemente selezionare tutte e 3 le tabelle quindi fare una order by? ( "per ordinare o limitare l'intero UNIONrisultato, SELECTmettere tra parentesi le singole istruzioni e inserire ORDER BYo LIMITdopo l'ultima" ). Confronta il tuo codice con i.stack.imgur.com/LpTMU.png
Pacerier

1
@Pacerier Se questa sintassi funziona con MySQL, fallo! La mia sintassi è un po 'più multipiattaforma, generalmente non lavoro con MySQL. In realtà lo preferisco, tuttavia, poiché funziona in casi più generalizzati, ad esempio, considera quando vuoi solo le prime dieci corrispondenze dall'ultima query UNION - penso che dovresti comunque ricorrere alla mia sintassi.
RedFilter

1
@Pacerier, considera anche che il valore in questo modo consente a "select *" in alto di essere invece "select id, add_date" e rimuove "rank" dai risultati se non vuoi vederli.
Dev Null

45

Puoi usare le sottoquery per fare questo:

select * from (select values1 from table1 order by orderby1) as a
union all
select * from (select values2 from table2 order by orderby2) as b

1
Tnx, buono per separare gli ordini. Inoltre possiamo avere una disposizione diversa se la clausola d'ordine fosse comune ma vogliamo che i risultati siano separati (invece di separare gli ordini): 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
Questo non è corretto : l' uso di ORDER BYper singole SELECTaffermazioni non implica nulla circa l'ordine in cui le righe compaiono nel risultato finale
shmosel

Hai ragione, la risposta accettata è anche quella corretta. È successo che funzionasse bene quando l'ho provato.
rickythefox

shmosel (e rickythefox) - union all non rimuove i duplicati, quindi non ha motivo di cambiare il tipo di selezione individuale ordinata. Questo è il motivo per cui ha funzionato per te in passato e perché funzionerà per te in futuro. L'ordine incasinato nel risultato finale è dovuto al fatto che UNION (DISTINCT) necessita di un qualche tipo di ordinamento per combinare le righe in modo efficiente. O più specificamente, ordina perché combina le righe in modo efficiente. Quindi puoi fare affidamento su union all per preservare le tue sottoquery.
Gerard ONeill

2
Funzionava benissimo per le versioni precedenti di MySQL, ora questo approccio NON funzionerà. Perché lo standard SQL non garantisce di preservare l'ordine delle sottoquery.
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

Funzionava, ma stranamente avevo bisogno di cambiare una cosa: dovevo dare un alias condiviso al campo che stava ordinando. Ha funzionato oltre a quello, grazie!
HoldOffHunger

9

Una query di unione può avere solo una ORDER BYclausola principale , IIRC. Per ottenere ciò, in ogni query che compone la UNIONquery più grande , aggiungi un campo che sarà l'unico campo in base a cui ordini per UNIONi ORDER BY.

Ad esempio, potresti avere qualcosa di simile

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

Quel union_sortcampo può essere qualsiasi cosa tu voglia ordinare. In questo esempio, capita solo di inserire i risultati della prima tabella, della seconda tabella, ecc.


9

Non dimenticare che l'unione di tutti è un modo per aggiungere record a un set di record senza ordinarli o unirli (al contrario dell'unione).

Quindi per esempio:

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

Mantiene più chiare le singole query e consente di ordinare in base a parametri diversi in ciascuna query. Tuttavia, utilizzando il modo in cui la risposta selezionata potrebbe diventare più chiara a seconda della complessità e della correlazione dei dati, poiché si sta concettualizzando l'ordinamento. Consente inoltre di restituire la colonna artificiale al programma di query in modo che abbia un contesto in base al quale può ordinare o organizzare.

Ma in questo modo si ha il vantaggio di essere veloce, non introdurre variabili aggiuntive e rendere facile separare ogni query incluso l'ordinamento. La possibilità di aggiungere un limite è semplicemente un bonus extra.

E ovviamente sentiti libero di trasformare l'unione in un'unione e aggiungere un ordinamento per l'intera query. Oppure aggiungi un ID artificiale, nel qual caso in questo modo è facile ordinare in base a parametri diversi in ciascuna query, ma per il resto è uguale alla risposta accettata.


4

Ho ottenuto questo lavoro su un join plus union.

(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

3

Quando si utilizza una ORDER BYclausola all'interno di una query secondaria utilizzata insieme a un UNIONmysql, ottimizzerà il fileORDER BY clausola .

Questo perché per impostazione predefinita a UNIONrestituisce una lista non ordinata quindi quindi un fileORDER BY non farebbe nulla.

L'ottimizzazione è menzionata nei documenti e dice:

Per applicare ORDER BY o LIMIT a un singolo SELECT, inserisci la clausola tra parentesi che racchiudono 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);

Tuttavia, l'uso di ORDER BY per singole istruzioni SELECT non implica nulla riguardo all'ordine in cui le righe vengono visualizzate nel risultato finale perché UNION per impostazione predefinita produce un insieme di righe non ordinato. Pertanto, l'uso di ORDER BY in questo contesto è tipicamente in congiunzione con LIMIT, in modo che venga utilizzato per determinare il sottoinsieme delle righe selezionate da recuperare per SELECT, anche se non influisce necessariamente sull'ordine di quelle righe nel risultato finale UNION. Se ORDER BY appare senza LIMIT in una SELECT, è ottimizzato perché non avrà comunque alcun effetto.

L'ultima frase di questo è un po 'fuorviante perché dovrebbe avere un effetto. Questa ottimizzazione causa un problema quando ci si trova in una situazione in cui è necessario ordinare all'interno della sottoquery.

Per forzare MySQL a non eseguire questa ottimizzazione, puoi aggiungere una clausola LIMIT in questo modo:

(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 alto LIMITsignifica che potresti aggiungere un OFFSETsulla query complessiva se vuoi fare qualcosa come l'impaginazione.

Questo ti dà anche l'ulteriore vantaggio di poter ORDER BYdisporre di colonne diverse per ogni unione.


0

Provare:

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

Dove [QUERY 1] e [QUERY 2] sono le tue due query che desideri unire.


0

Questo perché stai ordinando l'intero set di risultati, dovresti ordinare, ogni parte dell'unione separatamente, oppure puoi usare ORDER BY (Something ie. Subquery distance) THEN (something ie row id) clause


0

Ho provato ad aggiungere l'ordine di a ciascuna delle query prima dell'unione di like

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

ma non sembrava funzionare. In realtà non ha eseguito l'ordine all'interno delle righe da ciascuna selezione.

Penso che dovrai mantenere l'ordine per all'esterno e aggiungere le colonne nella clausola where all'ordine per, qualcosa di simile

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

Questo potrebbe essere un po 'complicato, dal momento che vuoi raggruppare per intervalli, ma penso che dovrebbe essere fattibile.


user151841 idea union_sort di seguito sarebbe un bel modo per gestire il raggruppamento
Mike C
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.