Używanie union i order by clause w mysql

123

Chcę użyć kolejności według z unią w kwerendzie mysql. Pobieram różne typy rekordów na podstawie różnych kryteriów z tabeli opartej na odległości dla wyszukiwania w mojej witrynie. Pierwsze zapytanie wybierające zwraca dane związane z dokładnym wyszukiwaniem miejsca. Drugie zapytanie wybierające zwraca dane dotyczące odległości w promieniu 5 km od wyszukiwanego miejsca. Trzecie zapytanie wybierające zwraca dane dotyczące odległości w promieniu 5-15 km od wyszukiwanego miejsca.

Następnie używam union do scalenia wszystkich wyników i wyświetlenia ich na stronie ze stronicowaniem. Pod odpowiednim nagłówkiem „Dokładne wyniki wyszukiwania” , „Wyniki w promieniu 5 km” itp

Teraz chcę posortować wyniki na podstawie identyfikatora lub add_date. Ale kiedy dodam klauzulę order by na końcu mojego zapytania (query1 union query 2 union query 3 order by add_date). Sortuje wszystkie wyniki. Ale to, czego chcę, to sortowanie pod każdym nagłówkiem.

Aditya
źródło
Jakiego typu są pola, według których chcesz sortować w każdej tabeli?
user151841

Odpowiedzi:

222

Możesz to zrobić, dodając pseudokolumnę o nazwie rank do każdego wyboru, według której możesz najpierw posortować, przed sortowaniem według innych kryteriów, np .:

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
RedFilter
źródło
9
Dlaczego jest ai koniec?
Uday Hiwarale
25
MySQL wymaga, aby tabele pochodne miały alias.
RedFilter
4
@RedFilter, nie do końca rozumiem. Jaki jest sens „wybierz wszystko jako tabelę pochodną”, kiedy możemy po prostu zaznaczyć wszystkie 3 tabele, a następnie wykonać order by? ( „aby posortować lub ograniczyć cały UNIONwynik, SELECTumieść poszczególne instrukcje w nawiasach i umieść ORDER BYlub LIMITpo ostatniej” ). Porównaj swój kod z i.stack.imgur.com/LpTMU.png
Pacerier
1
@Pacerier Jeśli ta składnia działa z MySQL, zrób to! Moja składnia jest trochę bardziej wieloplatformowa, generalnie nie pracuję z MySQL. Właściwie to wolę, ponieważ działa w bardziej uogólnionych przypadkach, np. Rozważ, kiedy chcesz tylko pierwszych dziesięć dopasowań z ostatniego zapytania UNION - myślę, że i tak musiałbyś wrócić do mojej składni.
RedFilter
1
@Pacerier, weź pod uwagę również, że w ten sposób wartość „select *” na górze może zamiast tego mieć wartość „select id, add_date” i usunąć pozycję „rank” z wyników, jeśli nie chcesz ich widzieć.
Dev Null
45

Aby to zrobić, możesz użyć podzapytań:

select * from (select values1 from table1 order by orderby1) as a
union all
select * from (select values2 from table2 order by orderby2) as b
rickythefox
źródło
1
Tnx, dobry do rozdzielania zamówień. Możemy również mieć inny układ, jeśli klauzula kolejności była wspólna, ale chcemy, aby wyniki były oddzielone (zamiast oddzielania zamówień): 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
To jest niepoprawne : użycie ORDER BYdla poszczególnych SELECTstwierdzeń nie oznacza nic o kolejności, w jakiej wiersze pojawiają się w wyniku końcowym
shmosel
Masz rację, przyjęta odpowiedź jest również poprawna. To po prostu działało dobrze, kiedy go testowałem.
rickythefox
shmosel (i rickythefox) - union all nie usuwa duplikatów, więc nie ma powodu, aby zmieniać rodzaj twoich indywidualnych posortowanych selekcji. To dlatego działało dla Ciebie w przeszłości i dlaczego będzie działać dla Ciebie w przyszłości. Zepsuta kolejność w wyniku końcowym wynika z tego, że UNION (DISTINCT) potrzebuje jakiegoś rodzaju, aby efektywnie łączyć wiersze. A dokładniej, sortuje, ponieważ efektywnie łączy wiersze. Możesz więc polegać na union all, aby zachować podzapytania.
Gerard ONeill
2
To działało świetnie dla starszych wersji MySQL, teraz to podejście NIE zadziała. Ponieważ standard SQL nie gwarantuje zachowania kolejności podzapytań.
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
Mitali
źródło
To zadziałało, ale, co dziwne, musiałem zmienić jedną rzecz: musiałem podać wspólny alias dla pola, które było zamawiane. Poza tym pracował, dzięki!
HoldOffHunger
9

Zapytanie składające może mieć tylko jedną ORDER BYklauzulę główną , IIRC. Aby to uzyskać, w każdej kwerendy tworzącej większą UNIONzapytanie, dodać pole to będzie jedno pole sortować według dla UNION„s ORDER BY.

Na przykład możesz mieć coś takiego

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

To union_sortpole może zawierać dowolne informacje, według których chcesz sortować. W tym przykładzie zdarza się, że najpierw umieszcza się wyniki z pierwszej tabeli, drugiej tabeli itd.

user151841
źródło
9

Nie zapominaj, że łączenie wszystkich jest sposobem dodawania rekordów do zestawu rekordów bez sortowania lub scalania (w przeciwieństwie do sumowania).

Na przykład:

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

Zapewnia przejrzystość poszczególnych zapytań i umożliwia sortowanie według różnych parametrów w każdym zapytaniu. Jednak korzystając z wybranej odpowiedzi, może stać się jaśniejsze w zależności od złożoności i stopnia powiązania danych, ponieważ konceptualizujesz sortowanie. Umożliwia także zwrócenie sztucznej kolumny do programu wysyłającego zapytania, aby miała kontekst, według którego może sortować lub organizować.

Ale ten sposób ma tę zaletę, że jest szybki, nie wprowadza dodatkowych zmiennych i ułatwia oddzielenie każdego zapytania, w tym sortowania. Możliwość dodania limitu to po prostu dodatkowy bonus.

I oczywiście możesz zamienić całą unię w sumę i dodać sortowanie dla całego zapytania. Lub dodaj sztuczny identyfikator, w którym to przypadku ułatwi to sortowanie według różnych parametrów w każdym zapytaniu, ale poza tym jest to to samo, co zaakceptowana odpowiedź.

Gerard ONeill
źródło
4

Mam to, pracując nad związkiem typu Join plus.

(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
Robert Saylor
źródło
3

Użycie ORDER BYklauzuli wewnątrz zapytania podrzędnego w połączeniu z UNIONmysql zoptymalizuje ORDER BYklauzulę.

Dzieje się tak, ponieważ domyślnie a UNIONzwraca nieuporządkowaną listę, więc ORDER BYnie zrobiłby nic.

Optymalizacja jest wspomniana w dokumentacji i mówi:

Aby zastosować ORDER BY lub LIMIT do pojedynczego polecenia SELECT, umieść klauzulę w nawiasach, które otaczają 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);

Jednak użycie ORDER BY dla poszczególnych instrukcji SELECT nie oznacza nic o kolejności, w jakiej wiersze pojawiają się w wyniku końcowym, ponieważ funkcja UNION domyślnie tworzy nieuporządkowany zestaw wierszy. Dlatego użycie ORDER BY w tym kontekście jest zwykle w połączeniu z LIMIT, więc jest używane do określenia podzbioru wybranych wierszy do pobrania dla polecenia SELECT, nawet jeśli niekoniecznie wpływa na kolejność tych wierszy w ostateczny wynik UNION. Jeśli ORDER BY pojawia się bez LIMIT w SELECT, jest zoptymalizowany, ponieważ i tak nie będzie miał żadnego efektu.

Ostatnie zdanie jest trochę mylące, ponieważ powinno mieć skutek. Ta optymalizacja powoduje problem w sytuacji, w której musisz złożyć zamówienie w podzapytaniu.

Aby zmusić MySQL do nie przeprowadzania tej optymalizacji, możesz dodać klauzulę LIMIT w następujący sposób:

(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)

Wysoka LIMIToznacza, że ​​możesz dodać OFFSETdo ogólnego zapytania, jeśli chcesz zrobić coś, na przykład paginację.

Daje to również dodatkową korzyść w postaci możliwości ORDER BYróżnych kolumn dla każdej unii.

hdifen
źródło
0

Próbować:

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

Gdzie [QUERY 1] i [QUERY 2] to dwa zapytania, które chcesz scalić.

André Hoffmann
źródło
0

Dzieje się tak, ponieważ sortujesz cały zbiór wyników, powinieneś sortować każdą część unii osobno, lub możesz użyć klauzuli ORDER BY (coś, np. Odległość podzapytania) THEN (coś, czyli identyfikator wiersza)

canni
źródło
0

Próbowałem dodać kolejność do każdego z zapytań przed połączeniem, np

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

ale to nie działało. W rzeczywistości nie uporządkował w wierszach z każdego wyboru.

Myślę, że będziesz musiał zachować kolejność na zewnątrz i dodać kolumny w klauzuli Where do zamówienia, coś w stylu

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

Może to być trochę trudne, ponieważ chcesz grupować według zakresów, ale myślę, że powinno to być wykonalne.

Mike C.
źródło
user151841 idea union_sort poniżej byłaby dobrym sposobem obsługi grupowania
Mike C