Использование предложения union и order by в mysql


123

Я хочу использовать заказ с объединением в запросе mysql. Я получаю разные типы записей на основе разных критериев из таблицы на основе расстояния для поиска на моем сайте. Первый запрос выбора возвращает данные, относящиеся к точному поиску места. Второй запрос выбора возвращает данные, относящиеся к расстоянию в пределах 5 км от места поиска. Третий запрос выбора возвращает данные, относящиеся к расстоянию в пределах 5-15 км от места поиска.

Затем я использую объединение, чтобы объединить все результаты и показать на странице с разбиением на страницы. Под соответствующим заголовком "Точные результаты поиска" , "Результаты в пределах 5 км" и т. Д.

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


По какому типу поля вы хотите отсортировать в каждой таблице?
user151841

Ответы:


222

Вы можете сделать это, добавив псевдостолбец с именем rank к каждому выбору, который вы можете сначала отсортировать, а затем отсортировать по другим критериям, например:

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и конец?
Удай Хиварале

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
Аликс

18
Это неверно : использование ORDER BYдля отдельных SELECTоператоров ничего не
говорит

Вы правы, принятый ответ тоже правильный. Просто так получилось, что когда я это протестировал, это сработало.
rickythefox

shmosel (и rickythefox) - union all не удаляет дубликаты, и поэтому у него нет причин изменять вид ваших отдельных отсортированных выборок. Вот почему это сработало для вас в прошлом и почему это сработает для вас в будущем. Неправильный порядок в конечном результате связан с тем, что UNION (DISTINCT) требует какой-то сортировки для эффективного объединения строк. Или, более конкретно, сортирует, потому что эффективно объединяет строки. Таким образом, вы можете положиться на union all, чтобы сохранить свои подзапросы.
Gerard 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запрос, добавьте поле, которое будет единственным полем, по которому вы будете сортировать по UNIONэлементам 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он ничего не сделает.

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

Чтобы применить ORDER BY или LIMIT к отдельному SELECT, поместите предложение в круглые скобки, которые заключают 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 появляется без LIMIT в 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 (что-то, например, расстояние подзапроса) THEN (что-то, например идентификатор строки)


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)

но, похоже, это не сработало. На самом деле он не выполнял упорядочение строк для каждого выбора.

Я думаю, вам нужно будет сохранить порядок снаружи и добавить столбцы в предложении where к порядку, что-то вроде

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

Это может быть немного сложно, поскольку вы хотите сгруппировать по диапазонам, но я думаю, что это должно быть выполнимо.


user151841 Представленная ниже идея union_sort была бы хорошим способом справиться с группировкой
Майк Си,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.