Використання об'єднання та порядку за умовою в mysql


123

Я хочу використовувати замовлення by union у запиті mysql. Я отримую записи різних типів на основі різних критеріїв із таблиці, заснованої на відстані для пошуку на моєму сайті. Перший запит на вибір повертає дані, пов'язані з точним пошуком місця. Запит другого вибору повертає дані, що відносяться до відстані в 5 км від місця пошуку. Запит 3-го вибору повертає дані, що відносяться до відстані в межах 5-15 км від місця пошуку.

Тоді я використовую об'єднання, щоб об'єднати всі результати та показати на сторінці з підказками. Під відповідним заголовком як "Точні результати пошуку" , "Результати за 5 км" тощо

Тепер я хочу сортувати результати на основі id або add_date. Але коли я додаю порядок за пунктом наприкінці мого запиту (query1 union query 2 order query 3 order by add_date). Він сортує всі результати. Але те, що я хочу, це має сортувати під кожним заголовком.


За типом (ими) є / які поля, за якими потрібно сортувати у кожній таблиці?
користувач151841

Відповіді:


221

Це можна зробити, додавши до кожного вибору псевдо стовпчик з ім’ям ранжу, який ви можете сортувати за першим, перш ніж сортувати за іншими критеріями, наприклад:

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
Чому є aі кінець?
Uday Hiwarale

25
MySQL вимагає, щоб у похідних таблиць був псевдонім.
RedFilter

4
@RedFilter, я не дуже розумію. Який сенс "вибрати всіх як похідну таблицю", коли ми можемо просто вибрати всі 3 таблиці, а потім зробити order by? ( "для сортування або обмеження всього UNIONрезультату, в дужках окремих SELECTвисловлювань та розміщення останнього ORDER BYабо LIMITпісля останнього" ). Порівняйте свій код з i.stack.imgur.com/LpTMU.png
Pacerier

1
@Pacerier Якщо цей синтаксис працює з MySQL, переходьте до цього! Мій синтаксис трохи більше кросплатформенний, я взагалі не працюю з MySQL. Я насправді вважаю за краще це, як це працює в більш узагальнених випадках, наприклад, враховуйте, коли вам потрібно лише перші десять збігів з останнього запиту UNION - я думаю, вам все одно доведеться повернутися до мого синтаксису.
RedFilter

1
@Pacerier, також врахуйте, що таким чином значення дозволяє верхньому "select *", щоб замість цього було "select id, add_date" та зніміть "rank" з результатів, якщо ви не хотіли їх бачити.
Dev Null

45

Для цього можна використовувати підзапити:

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, Хороший для розділення замовлень. Також у нас може бути інша домовленість, якщо застереження про замовлення було загальним, але ми хочемо, щоб результати були розділені (замість розділення замовлень): 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
Це неправильно : використання ORDER BYдля окремих SELECTвисловлювань нічого не означає про порядок відображення рядків у кінцевому результаті
shmosel

Ви маєте рацію, прийнята відповідь також є правильною. Це просто вийшло добре, коли я тестував його.
rickythefox

shmosel (і rickythefox) - об'єднання все не видаляє дублікатів, і тому не має підстав змінювати тип обраного вами індивідуального сортування. Ось чому він працював на вас у минулому, і чому він буде працювати для вас у майбутньому. Порядок, що порушується в кінцевому результаті, пояснюється тим, що UNION (DISTINCT) потребує певного роду, щоб ефективно поєднувати рядки. Або, конкретніше, сортування, оскільки воно ефективно поєднує рядки. Таким чином, ви можете залежати від об'єднання всіх, щоб зберегти свої підзапити.
Джерард ONeill

2
Це працювало чудово для старих версій MySQL, тепер такий підхід НЕ працює. Оскільки стандарт SQL не гарантує збереження порядку підзапиту.
Андрій

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

Це спрацювало, але як не дивно, мені потрібно було змінити одне: мені потрібно було надати спільний псевдонім для поля, яке замовляв. Працював крім цього, дякую!
HoldOffHunger

9

Запит об’єднання може мати лише один головний ORDER BYпункт, IIRC. Щоб отримати це, у кожен запит, що складається з більшого UNIONзапиту, додайте поле, яке буде одним полем, яке ви сортуєте для UNIONs ORDER BY.

Наприклад, у вас може бути щось подібне

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

Це union_sortполе може бути будь-яким, що ви хочете відсортувати. У цьому прикладі просто буває помістити результати першої таблиці першої, другої таблиці другої тощо.


9

Не забувайте, об'єднання все - це спосіб додавання записів до набору записів без сортування чи об'єднання (на відміну від об'єднання).

Так, наприклад:

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

Це забезпечує чіткіші окремі запити і дозволяє сортувати за різними параметрами в кожному запиті. Однак, використовуючи обраний спосіб відповіді, це може стати зрозумілішим залежно від складності та наскільки пов’язані дані, оскільки ви розробляєте концепцію сортування. Це також дозволяє повернути штучну колонку до програми запитів, щоб у неї був контекст, який він може сортувати або упорядковувати.

Але цей спосіб має перевагу в тому, що він є швидким, не вводить зайві змінні та полегшує відокремлення кожного запиту, включаючи сортування. Можливість додавати ліміт - це просто додатковий бонус.

І звичайно сміливо перетворіть об'єднання на об'єднання та додайте сортування для всього запиту. Або додайте штучний ідентифікатор, і в цьому випадку цей спосіб полегшує сортування за різними параметрами в кожному запиті, але в іншому випадку це те саме, що і прийнята відповідь.


4

У мене це працює над об'єднанням "плюс плюс".

(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

Якщо ви використовуєте ORDER BYпункт усередині підзапиту, який використовується спільно з UNIONmysql, це оптимізує ORDER BYзастереження.

Це тому, що за замовчуванням a UNIONповертає не упорядкований список, тому, таким чином ORDER BY, нічого не буде робити.

Оптимізація згадується в документах і говорить:

Щоб застосувати ЗАМОВЛЕННЯ ВІД або ОБМЕЖЕННЯ до окремого 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);

Однак використання ORDER BY для окремих операторів SELECT нічого не означає про порядок відображення рядків у кінцевому результаті, оскільки UNION за замовчуванням створює не упорядкований набір рядків. Тому використання ORDER BY у цьому контексті, як правило, поєднується з LIMIT, так що воно використовується для визначення підмножини вибраних рядків для вибору SELECT, навіть якщо це не обов'язково впливає на порядок цих рядків у підсумковий результат UNION. Якщо ORDER BY відображається без ОБМЕЖЕННЯ в SELECT, він оптимізується, оскільки це не матиме жодного ефекту.

Останнє речення цього є дещо оманливим, оскільки воно повинно мати ефект. Ця оптимізація спричиняє проблеми, коли ви потрапляєте в ситуацію, коли вам потрібно замовити всередині підзапиту.

Щоб змусити MySQL не робити цієї оптимізації, ви можете додати пункт LIMIT так:

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

Високий LIMITозначає, що ви можете додати запит OFFSETдо загального запиту, якщо хочете зробити щось таке, як пагинація.

Це також дає додаткову перевагу від можливості мати ORDER BYрізні стовпці для кожного союзу.


0

Спробуйте:

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

Де [QUERY 1] та [QUERY 2] - ваші два запити, які ви хочете об'єднати.


0

Це відбувається тому, що ви сортуєте весь набір результатів, слід сортувати кожну частину об'єднання окремо, або ви можете використовувати ORDER BY (щось, тобто. Підзапит), після чого (щось, тобто ідентифікатор рядка)


0

Я спробував додати замовлення до кожного із запитів до об’єднання, як

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

але, схоже, це не спрацювало. Насправді це не було впорядкування в рядках з кожного вибору.

Я думаю, вам потрібно буде зберегти порядок на зовнішній стороні і додати стовпці в пункті де до наказу, щось подібне

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

Це може бути дещо хитро, оскільки ви хочете групуватись за діапазонами, але я думаю, що це має бути доцільним.


ідея user151841 union_sort нижче буде гарним способом впоратися з групуванням
Mike C
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.