【问题标题】:Union doesnt preserve order(not stable)联盟不保持秩序(不稳定)
【发布时间】:2016-11-23 23:04:32
【问题描述】:

我执行下面的查询并期望第一组然后第二组明显但是顺序是完全随机的。 我预期的结果:约翰、马克、戴夫、罗伯特、柯克

select *
from (
    select Name
    from (values ('john'),('mark'),('dave')) X(Name)
    union
    select Name
    from (values ('robert'),('mark'),('kirk')) X(Name)
) q

这是一个替代查询,我希望它有有序(稳定)的结果,但我得到了相同的结果。 Union All 按照我的预期附加第二组,但稍后应用 Distinct 中断排序。

select Distinct Name
from (
    select Name
    from (values ('john'),('mark'),('dave')) X(Name)
    union all
    select Name
    from (values ('robert'),('mark'),('kirk')) X(Name)
) q

什么是有序且不同的集合的解决方案?

【问题讨论】:

  • 添加order by。没有 order by,则无法保证 order by
  • @Nick.McDermaid 这并不像看起来那么简单。我不想要有序的结果。我想要明显结合的结果。
  • 使用 union 而不是 union all 用于不同的集合。如果您想保留两套完整的顺序(即第一套在第二套之上),那么您首先需要确定是否存在重复,您希望从哪一套中取出?
  • 例如在您的第一个示例中,为什么从第二组而不是第一组中取出标记?规则是什么 - 规则是“总是从第二组中取出重复项”
  • 使用排名函数并通过fiist中的行数添加到较低的集合中......或任何有效的tp强制执行顺序。您甚至可以考虑使用持久密钥,但我敢说我们首先缺少导致订单的要求。

标签: sql-server tsql sql-server-2016


【解决方案1】:
WITH q AS
(
    -- Original data
    SELECT Name FROM (VALUES ('john'),('mark'),('dave')) X(Name)
    UNION ALL
    SELECT Name FROM (VALUES ('robert'),('mark'),('kirk')) X(Name)
), r AS
(
    -- Add the sequence column for ordering
    -- ** It just use natual order **
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS Seq, * from q
), s AS
( 
    -- Use RN to filter out the duplicates
    SELECT *, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Seq) AS RN FROM r
)
SELECT Name FROM s WHERE RN = 1 ORDER BY Seq

【讨论】:

  • 这似乎是最佳答案。
【解决方案2】:

如果你想要第一个集合,然后第二个集合中不在第一个集合中的每个元素,那么你必须指定它。如果您想保证第一组中的内容在第二组之前列出,您还需要添加 ORDER BY。

select q.name
from (
    select Name,n
    from 
    (values ('john'),('mark'),('dave')) X(Name)
    cross join (values (1)) y(n)

    union all

    select Name,n
    from
    (
    select Name
    from (values ('robert'),('mark'),('kirk')) X(Name)

    except

    select Name 
    from
    (values ('john'),('mark'),('dave')) X(Name)

    ) Z(Name)
    Cross join (values (2)) y(n)

) q
order by q.n

【讨论】:

    【解决方案3】:

    如果我们使用值来选择数据,则无需使用 order by 子句,并且我们不必担心该特定查询的潜在索引更改,因此您将始终拥有订单,而且我们使用联合所有它只会连接结果集没有排序。

    但是,如果您使用的是表,是的,顺序肯定会发生变化,这取决于几个因素,例如表索引、返回的列、引入的新数据等。因此,如果您希望以特定方式对结果进行排序,则需要指定 ORDER BY 子句。

    对于您的示例,您不需要像下面这样使用 order by 子句

    select  Name
    from (
        select Name
        from (values ('john'),('mark'),('dave')) X(Name)
        union all
    
       ( select Name
        from (values ('robert'),('mark'),('kirk')) X(Name)
        except
        select Name
        from (values ('john'),('mark'),('dave')) X(Name))
    
    ) q
    

    【讨论】:

      【解决方案4】:

      您是否可以在内部 SELECT 语句中添加一个 SortOrder 列,然后按此排序?比如:

      select Distinct Name
      from (
          select SortOrder, Name
          from (values (1, 'john'),(2, 'mark'),(3, 'dave')) X(SortOrder, Name)
          union all
          select SortOrder, Name
          from (values (4, 'robert'),(2, 'mark'),(5, 'kirk')) X(SortOrder, Name)
      ) q
      order by SortOrder ASC
      

      【讨论】:

      • from 子句将被替换为真实表,因此这是不被接受的答案。如果这些是固定数据,那么我可以手动对它们进行明确排序。
      【解决方案5】:

      保证顺序的唯一方法是使用 ORDER BY 子句,这在 BOL 中有记录:

      https://msdn.microsoft.com/en-us/library/ms188385.aspx

      按指定的列列表对查询的结果集进行排序,并且可以选择将返回的行限制在指定范围内。除非指定了 ORDER BY 子句,否则无法保证结果集中的行返回顺序。

      如果您希望根据 UNION 中的顺序返回行,那么您可以执行以下操作:

      select Name
      from (
          select 1 as sort1, Name
          from (values ('john'),('mark'),('dave')) X(Name)
          union
          select 2 as sort1, Name
          from (values ('robert'),('mark'),('kirk')) X(Name)
      ) q
      order by sort1, Name
      

      【讨论】:

      • 您甚至正在对名称进行排序,所以它仍然不是预期的结果。请查看预期结果。
      • 其次结果不明显。
      猜你喜欢
      • 2018-04-17
      • 2017-05-29
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 2017-12-23
      • 1970-01-01
      • 2015-08-02
      • 1970-01-01
      相关资源
      最近更新 更多