代码之家  ›  专栏  ›  技术社区  ›  Igor Yalovoy

是可查询的。SQL Server数据库的OrderBy不稳定?

  •  2
  • Igor Yalovoy  · 技术社区  · 6 年前

    OrderBy 对于LINQ to对象是稳定的,但是 MSDN 在…上 Queryable.OrderBy 没有提到它是否稳定。

    我想这取决于提供者的实现。SQL Server是否不稳定?因为看起来是这样。我快速看了一下 Queryable source code ,但从那里看并不明显。

    在进行其他操作之前,我需要订购一个集合,并且我想使用 IQueryable 而不是 IEnumerable 为了性能。

    // All the timestamps are the same and I am getting inconsistent 
    // results by running it multiple times, first few pages return the same results
    var result = data.OrderBy(i => i.TimeStamp).Skip(start).Take(length);
    

    但如果我使用

    var result = data.ToList().OrderBy(i => i.TimeStamp).Skip(start).Take(length);
    

    它工作得很好,但我失去了从LINQ到SQL的性能提升。可查询的OrderBy/Skip/Take组合似乎会产生不一致的结果。

    生成的SQL代码在我看来很好:

    SELECT 
    ...
    FROM [dbo].[Table] AS [Extent1]
    ORDER BY [Extent1].[TimeStamp] ASC
    OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY 
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   arekzyla    6 年前

    在Linq到实体中,Linq查询被转换为SQL查询,所以OrderBy的Linq到对象实现并不重要。您应该查看ORDER BY的数据库实现。如果您使用的是MS SQL,您可以在文档中找到:

    要在使用偏移量和提取的查询请求之间获得稳定的结果,必须满足以下条件: (…)

    1. ORDER BY子句包含保证唯一的列或列组合。

    因此,相同值的ORDER BY不能保证相同的顺序,因此限制它可能会提供不同的结果集。要解决此问题,您只需按具有唯一值的其他列进行排序,例如id。因此,基本上您将有:

    var result = data
        .OrderBy(i => i.TimeStamp)
        .ThenBy(i => i.Id)
        .Skip(start)
        .Take(length);
    
        2
  •  0
  •   Jim Berg    6 年前

    我认为“稳定”是指始终如一。如果SQL查询中没有ORDER BY,则无法保证每次运行查询时数据的顺序。它只需以对服务器最有效的顺序返回所有数据。当您添加ORDER BY时,它将对该数据进行排序。由于您是在所有排序值都相同的情况下对数据进行排序,因此没有行被重新排序,因此排序后的数据的顺序是您不期望的。如果需要特定的顺序,则需要添加辅助排序列,如ID。

    最好不要假定从服务器返回的数据的顺序,除非您明确定义了该顺序。