在mysql中使用union和order by子句


123

我想在MySQL查询中通过Union使用order。我正在根据基于距离的表格从不同的标准中获取不同类型的记录,这些表格基于在我的网站上进行搜索的距离。第一个选择查询返回与精确位置搜索有关的数据。第二个选择查询返回与距搜索位置5公里以内的距离有关的数据。第三个选择查询返回与距搜索位置5-15公里之内的距离有关的数据。

然后,即时消息使用union合并所有结果,并在带有分页的页面上显示。在适当的标题下,例如“精确搜索结果”“ 5公里内的结果”

现在我想基于id或add_date对结果进行排序。但是,当我在查询末尾添加order by子句时(query1联合查询2联合查询3由add_date排序)。它对所有结果进行排序。但是我想要的是应该在每个标题下进行排序。


您要在每个表格中对字段进行排序的是什么类型?
user151841

Answers:


221

您可以通过向每个选择添加一个名为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和结束?
Uday Hiwarale 2013年

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,好一个单独的订单。同样,如果order子句是公共的,我们可以有不同的安排,但是我们希望结果分开(而不是分开订单): 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 2013年

18
这是不正确的for 语句的使用ORDER BYSELECT
shmosel

您说得对,接受的答案也是正确的答案。当我对其进行测试时,它恰好运行良好。
rickythefox '16

shmosel(和rickythefox)-工会都不会删除重复项,因此没有理由更改个人排序选择的种类。这就是为什么它在过去为您服务,以及为什么将来将为您服务的原因。最终结果中混乱的顺序是由于UNION(DISTINCT)需要某种排序来有效地组合行。或更具体地说,进行排序是因为它有效地合并了行。因此,您可以依靠全部并集来保留子查询。
Gerard ONeill

2
这是较早的MySQL版本工作的伟大,现在这种做法将工作。因为SQL标准不保证保留子查询的顺序。
安德烈(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

这行得通,但奇怪的是,我需要更改一件事:我需要给排序依据的字段提供共享别名。除此之外,谢谢!
HoldOffHunger

9

联合查询只能有一个主ORDER BY子句IIRC。为了得到这一点,在每个查询组成更大的UNION查询中,添加一个字段,这将是你的一个字段排序UNIONORDER 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

它使各个查询更清晰,并允许您按每个查询中的不同参数进行排序。但是,通过使用所选答案的方式,它可能会变得更加清晰,具体取决于复杂性和数据的关联性,因为您正在概念化排序。它还允许您将人工列返回到查询程序,以便它具有可以按排序或组织的上下文。

但是这种方式的优点是速度快,不引入额外的变量,并且易于分离出每个查询(包括排序)。增加限制的能力仅仅是额外的奖励。

当然,您可以随意将所有的联合变成一个联合,并为整个查询添加一个排序。或添加一个人工ID,在这种情况下,这种方式可以轻松地按每个查询中的不同参数进行排序,但与接受的答案相同。


4

我让这个工作加入了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

当您ORDER BY在与UNIONmysql 结合使用的子查询中使用ORDER BY子句时,将优化该子句。

这是因为默认情况下,a UNION返回无序列表,因此a 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);

但是,对单个SELECT语句使用ORDER BY并不意味着行在最终结果中出现的顺序,因为UNION默认情况下会产生一组无序的行。因此,在这种情况下,ORDER BY的使用通常与LIMIT结合使用,因此,它可以用于确定要为SELECT检索的所选行的子集,即使它不一定影响SELECT中的那些行的顺序。最终的UNION结果。如果ORDER BY在SELECT中没有LIMIT出现,则会对其进行优化,因为它始终无效。

最后一句话有点误导,因为它应该起作用。当您需要在子查询中进行排序时,这种优化会导致问题。

要强制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(即行ID)子句


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的想法将是处理分组的一种好方法
Mike C 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.