Usando union e order by cláusula no mysql


123

Eu quero usar a ordem com a união na consulta mysql. Estou buscando diferentes tipos de registro com base em critérios diferentes de uma tabela com base na distância para uma pesquisa no meu site. A primeira consulta de seleção retorna dados relacionados à pesquisa exata do local. A 2ª consulta de seleção retorna dados relacionados à distância dentro de 5 km do local pesquisado. A terceira consulta de seleção retorna dados relacionados à distância entre 5 e 15 km do local pesquisado.

Em seguida, estou usando union para mesclar todos os resultados e mostrar em uma página com paginação. Sob o cabeçalho apropriado como 'Resultados de pesquisa exata' , 'Resultados dentro de 5 km' etc.

Agora eu quero classificar os resultados com base no id ou add_date. Mas quando adiciono ordem por cláusula no final da minha consulta (query1 union query 2 union query 3 order by add_date). Classifica todos os resultados. Mas o que eu quero é que deve classificar em cada título.


Que tipo (s) são / são os campos que você deseja classificar em cada tabela?
user151841

Respostas:


221

Você pode fazer isso adicionando uma pseudo-coluna denominada rank a cada seleção, para classificar primeiro, antes de classificar por outros critérios, por exemplo:

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
Por que existe ae o fim?
Uday Hiwarale

25
O MySQL requer que as tabelas derivadas tenham um alias.
RedFilter

4
@ RedFilter, eu não entendo direito. Qual é o objetivo de "selecionar tudo como uma tabela derivada" quando podemos simplesmente selecionar todas as 3 tabelas e executar um order by? ( "para classificar ou limitar o UNIONresultado inteiro , entre parênteses as SELECTinstruções individuais e coloque a ORDER BYou LIMITapós a última" ). Compare seu código com i.stack.imgur.com/LpTMU.png
Pacerier

1
@Pacerier Se essa sintaxe funcionar com MySQL, vá em frente! Minha sintaxe é um pouco mais multiplataforma, geralmente não trabalho com o MySQL. Na verdade, eu prefiro isso, pois funciona em casos mais generalizados, por exemplo, considere quando você deseja apenas as dez primeiras correspondências da última consulta UNION - acho que você teria que voltar à minha sintaxe de qualquer maneira.
RedFilter

1
@Pacerier, considere também que o valor dessa maneira permite que o "select *" superior seja "select id, add_date" e retire "rank" dos resultados, se você não quiser vê-los.
Dev nulo

45

Você pode usar subconsultas para fazer isso:

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, bom para separar ordens. Também podemos ter um arranjo diferente se a cláusula de ordem era comum, mas queremos que os resultados separados (em vez de separar ordens): 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
Esta é incorreta : uso de ORDER BYpara individuais SELECTdeclarações implica nada sobre a ordem em que as linhas aparecem no resultado final
shmosel

Você está certo, a resposta aceita também é a correta. Isso só funcionou bem quando eu testei.
Rickythefox 29/03

shmosel (e rickythefox) - union all não remove duplicatas e, portanto, não tem motivos para alterar o tipo de seleção individual. É por isso que funcionou para você no passado e porque funcionará para você no futuro. A ordem que está sendo alterada no resultado final deve-se ao fato de UNION (DISTINCT) precisar de algum tipo de classificação para combinar linhas com eficiência. Ou, mais especificamente, classifica porque combina linhas com eficiência. Portanto, você pode depender da união de todos para preservar suas subconsultas.
Gerard ONeill

2
Isso estava funcionando muito bem para versões mais antigas do MySQL, agora essa abordagem NÃO funcionará. Como o padrão SQL não garante a preservação da ordem da subconsulta.
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

Isso funcionou, mas estranhamente eu precisava mudar uma coisa: precisava dar um alias compartilhado ao campo que estava sendo solicitado. Trabalhou além disso, obrigado!
HoldOffHunger 13/09/16

9

Uma consulta de união pode ter apenas uma ORDER BYcláusula mestre , IIRC. Para conseguir isso, em cada consulta que compõem a maior UNIONconsulta, adicione um campo que vai ser o único campo você classificar por para os UNION's ORDER BY.

Por exemplo, você pode ter algo como

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

Esse union_sortcampo pode ser qualquer coisa que você queira classificar. Neste exemplo, acontece apenas de colocar os resultados da primeira tabela em primeiro lugar, da segunda tabela em segundo, etc.


9

Não se esqueça, a união all é uma maneira de adicionar registros a um conjunto de registros sem classificar ou mesclar (em oposição à união).

Então, por exemplo:

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

Ele mantém as consultas individuais mais claras e permite que você classifique por diferentes parâmetros em cada consulta. No entanto, usando o caminho da resposta selecionada, pode ficar mais claro, dependendo da complexidade e da relação dos dados, porque você está conceituando a classificação. Ele também permite que você retorne a coluna artificial ao programa de consulta para que ele tenha um contexto que possa ser classificado ou organizado.

Mas dessa maneira, tem a vantagem de ser rápido, não introduzir variáveis ​​extras e facilitar a separação de cada consulta, incluindo a classificação. A capacidade de adicionar um limite é simplesmente um bônus extra.

E, é claro, fique à vontade para transformar a união em união e adicionar uma classificação para toda a consulta. Ou adicione um ID artificial; nesse caso, dessa maneira, será fácil classificar por parâmetros diferentes em cada consulta, mas, caso contrário, será o mesmo que a resposta aceita.


4

Eu consegui isso trabalhando em uma união mais união.

(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 você usa uma ORDER BYcláusula dentro de uma subconsulta usada em conjunto com um UNIONmysql, ela otimizará a ORDER BYcláusula.

Isso ocorre porque, por padrão, a UNIONretorna uma lista não ordenada e, portanto, ORDER BYnão faria nada.

A otimização é mencionada nos documentos e diz:

Para aplicar ORDER BY ou LIMIT a um SELECT individual, coloque a cláusula entre parênteses que incluem o 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);

No entanto, o uso de ORDER BY para instruções SELECT individuais não implica nada na ordem em que as linhas aparecem no resultado final, porque UNION, por padrão, produz um conjunto não ordenado de linhas. Portanto, o uso de ORDER BY neste contexto é tipicamente em conjunto com LIMIT, para que seja usado para determinar o subconjunto das linhas selecionadas a serem recuperadas para o SELECT, mesmo que isso não afete necessariamente a ordem dessas linhas no resultado final da UNIÃO. Se ORDER BY aparecer sem LIMIT em um SELECT, ele será otimizado porque ele não terá efeito.

A última frase disso é um pouco enganadora, porque deve ter um efeito. Essa otimização causa um problema quando você está em uma situação em que precisa solicitar na subconsulta.

Para forçar o MySQL a não fazer essa otimização, você pode adicionar uma cláusula LIMIT da seguinte forma:

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

Um valor alto LIMITsignifica que você pode adicionar uma OFFSETconsulta geral se desejar fazer algo como paginação.

Isso também oferece o benefício adicional de poder ORDER BYcolunas diferentes para cada união.


0

Experimentar:

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

Onde [QUERY 1] e [QUERY 2] são suas duas consultas que você deseja mesclar.


0

Isso ocorre porque você está classificando todo o conjunto de resultados, deve classificar todas as partes da união separadamente ou pode usar a cláusula ORDER BY (algo como distância da subconsulta) THEN (algo como código da linha)


0

Tentei adicionar o pedido em cada uma das consultas antes da união, como

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

mas não parecia funcionar. Na verdade, ele não fazia o pedido nas linhas de cada seleção.

Acho que você precisará manter a ordem do lado de fora e adicionar as colunas na cláusula where à ordem de, algo como

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

Isso pode ser um pouco complicado, já que você deseja agrupar por intervalos, mas acho que deve ser possível.


A ideia user151841 union_sort abaixo seria uma boa maneira de lidar com o agrupamento #
Mike C
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.