如何在UNION中使用ORDER BY

时间:2014-07-10 18:26:41

标签: mysql sql

我想在每个UNION ALL查询中使用ORDER BY,但我无法找出正确的语法。这就是我想要的:

(
SELECT id, user_id, other_id, name 
FROM tablename 
WHERE user_id = 123 AND user_in IN (...) 
ORDER BY name
)
UNION ALL
(
SELECT id, user_id, other_id, name 
FROM tablename 
WHERE user_id = 456 AND user_id NOT IN (...) 
ORDER BY name
)

编辑: 需要明确的是:我需要两个这样的有序列表,而不是一个:

1 2 3 1 2 3 4 5

非常感谢!

4 个答案:

答案 0 :(得分:7)

你最后只使用一个ORDER BY。

联盟将两个选项转换为一个逻辑选择。 order-by适用于整个集合,而不适用于每个部分。

也不要使用任何parens。只是:

SELECT 1 as Origin, blah blah FROM foo WHERE x
UNION ALL
SELECT 2 as Origin, blah blah FROM foo WHERE y
ORDER BY Origin, z

答案 1 :(得分:7)

这样的东西应该在MySQL中起作用:

SELECT a.*
  FROM ( 
         SELECT ...  FROM ... ORDER BY ... 
       ) a
 UNION ALL 
SELECT b.*
  FROM ( 
         SELECT ...  FROM ... ORDER BY ... 
       ) b

但请注意,如果最外层查询中没有 ORDER BY (或GROUP BY)子句,则返回行的顺序为 NOT 保证。

如果需要按特定顺序返回的行,则应在最外层的查询中包含ORDER BY。在很多用例中,我们可以在最外层的查询中使用ORDER BY来满足结果。

但是,如果您有一个用例,您需要在第二个查询的所有行之前返回的第一个查询中的所有行,则一个选项是在每个查询中包含一个额外的鉴别器列。例如,在第一个查询中添加 ,'a' AS src ,'b' AS src 添加到第二个查询。

然后,最外面的查询可以包含 ORDER BY src, name ,以保证结果的顺序。


<强>后续

在原始查询中,优化程序会丢弃查询中的ORDER BY;由于没有ORDER BY应用于外部查询,MySQL可以按照自己想要的顺序自由返回行。

我的答案中的查询中的“技巧”(上图)取决于某些版本的MySQL特有的行为。

测试用例:

填充表格

CREATE TABLE foo2 (id INT PRIMARY KEY, role VARCHAR(20)) ENGINE=InnoDB;
CREATE TABLE foo3 (id INT PRIMARY KEY, role VARCHAR(20)) ENGINE=InnoDB;

INSERT INTO foo2 (id, role) VALUES 
  (1,'sam'),(2,'frodo'),(3,'aragorn'),(4,'pippin'),(5,'gandalf');
INSERT INTO foo3 (id, role) VALUES 
  (1,'gimli'),(2,'boromir'),(3,'elron'),(4,'merry'),(5,'legolas');

查询

SELECT a.*
  FROM ( SELECT s.id, s.role
           FROM foo2 s
          ORDER BY s.role
       ) a
 UNION ALL
SELECT b.*
  FROM ( SELECT t.id, t.role
           FROM foo3 t
          ORDER BY t.role
       ) b

结果集已返回

    id  role     
 ------  ---------
      3  aragorn  
      2  frodo    
      5  gandalf  
      4  pippin   
      1  sam      
      2  boromir  
      3  elron    
      1  gimli    
      5  legolas  
      4  merry    

foo2中的行按顺序返回,然后是foo3中的行,再次按“顺序”返回。

请注意(再次)此行为 NOT 保证。 (我们观察者的行为是MySQL如何处理内联视图(派生表)的副作用。在5.5之后的版本中,这种行为可能会有所不同。)

如果您需要按特定顺序返回的行,请为最外层查询指定 ORDER BY 子句。该顺序将适用于整个结果集。

正如我前面提到的,如果我首先需要第一个查询中的行,然后是第二个查询,我会在每个查询中包含一个“discriminator”列,然后在ORDER BY子句中包含“discriminator”列。我也会废除内联视图,并执行以下操作:

SELECT s.id, s.role, 's' AS src
  FROM foo2 s
 UNION ALL
SELECT t.id, t.role, 't' AS src
  FROM foo3 t
 ORDER BY src, role

答案 2 :(得分:4)

请勿在{{1​​}}内的单个ORDER BY声明中使用SELECT,除非您正在使用UNION

MySQL docs on UNION解释了为什么(强调我的):

  

要将ORDER BY或LIMIT应用于单个SELECT,请放置该子句   在括起SELECT的括号内:

LIMIT
     

但是,对单个SELECT语句使用ORDER BY意味着   没有关于行在最终结果中出现的顺序   因为UNION默认生成一组无序行。因此,   在这种情况下,ORDER BY的使用通常与   LIMIT,以便用于确定所选行的子集   检索SELECT,即使它不一定影响   最终UNION结果中这些行的顺序。 如果出现ORDER BY   在SELECT中没有LIMIT,它会被优化掉,因为它会有   无论如何都没有效果。

     

使用ORDER BY或LIMIT子句对整个UNION进行排序或限制   结果,括起单个SELECT语句并放置   最后一个之后的ORDER BY或LIMIT。以下示例使用两者   子句:

(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 a FROM t1 WHERE a=10 AND B=1) UNION (SELECT a FROM t2 WHERE a=11 AND B=2) ORDER BY a LIMIT 10; 条款可以为您提供所需的内容:

ORDER BY

答案 3 :(得分:0)

      (SELECT id, user_id, other_id, name
         FROM tablename
        WHERE user_id = 123
          AND user_in IN (...))
UNION ALL
      (SELECT id, user_id, other_id, name
         FROM tablename
        WHERE user_id = 456
          AND user_id NOT IN (...)))
 ORDER BY name

您还可以简化此查询:

SELECT id, user_id, other_id, name
  FROM tablename
 WHERE (user_id = 123 AND user_in IN (...))
    OR (user_id = 456 AND user_id NOT IN (...))