【问题标题】:LINQ OrderBy. Does it always return the same ordered list?LINQ 排序依据。它总是返回相同的有序列表吗?
【发布时间】:2014-11-13 08:46:23
【问题描述】:

我正在尝试一个简单的 OrderBy 语句。

要订购的目标数据如下:

[
  {"id":40, "description":"aaa", "rate":1},
  {"id":1,  "description":"bbb", "rate":1},
  {"id":4,  "description":"ccc", "rate":2},
  {"id":19, "description":"aaa", "rate":1} 
]

然后我按 rate 属性订购商品。

奇怪的是,如果我“订购”它们,它会通过给定的偏移量“跳过”一些项目,然后只“获取”部分数据。

例如,

var result = items.OrderBy(i => i.rate);
var result = result.Skip(2);
var result = result.Take(2);

结果大部分看起来都很好,但根本没有返回“边缘情况”项。

例如,

如果第一个结果返回为

    [{"id":40, "description":"aaa", "rate":1}, {"id":1, "description":"bbb", "rate":1}]

第二个结果像

    [{"id":1, "description":"bbb", "rate":1}, {"id":4, "description":"ccc", "rate":2}]

第二个查询调用没有返回项目“id: 19”。而是项目“id: 1”返回了两次。

我的猜测是,SQL OrderBy 语句不会在每次 OrderBy 对给定属性进行排序时生成相同的排序列表,但是共享相同属性的组内的确切顺序可能会发生变化。

引擎盖下的确切机制是什么?

【问题讨论】:

  • 如果您不指定顺序,则无法保证。如果您指定多个列,例如order by rate, description,然后它将按rate 排序,然后rate 值重复,descriptionratedescription 可能仍然有多个具有相同值的行,并且它们的顺序将保持未指定。 id 经常被用作平局以确保稳定的订单:order by rate, description, id
  • 顺序可能不同的一个实际原因:一些快速排序算法会随机选择枢轴。

标签: sql .net sql-server linq linq-to-objects


【解决方案1】:

简短回答: LINQ to Objects 使用稳定的排序算法,因此我们可以说它是确定性的,而 LINQ to SQL 依赖于通常是非确定性的 Order By 的数据库实现。 p>

确定性排序算法是一种在不同运行中始终具有相同行为的算法。

在您的示例中,您的 OrderBy 子句中有重复项。对于保证和预测排序,其中一个顺序子句或顺序子句的组合必须是唯一的

在 LINQ 中,您可以通过添加另一个 OrderBy 子句来引用您的唯一属性来实现它,例如 in
items.OrderBy(i => i.Rate).ThenBy(i => i.ID)

长答案:

LINQ to Objects 使用稳定的排序,如以下链接所述:MSDN

在 LINQ to SQL 中,它取决于底层数据库的排序算法,通常是不稳定的排序,例如 MS SQL Server (MSDN)。

在稳定排序中,如果两个元素的键相等,则保留元素的顺序。相反,不稳定的排序不会保留具有相同键的元素的顺序。

因此,对于 LINQ to SQL,排序通常是非确定性的,因为 RDMS(关系数据库管理系统,如 MS SQL Server)可能直接使用带有随机枢轴选择的不稳定排序算法或随机性可能与数据库碰巧首先访问文件系统中的哪一行有关。

例如,假设文件系统中的页面大小最多可以容纳 4 行。

如果您插入以下数据,页面将被填满:

     Page 1
| Name | Value |
|------|-------|
|   A  |   1   |
|   B  |   2   |
|   C  |   3   |
|   D  |   4   |


如果需要插入新行,RDMS 有两种选择:

  1. 创建一个新页面来分配新行。
  2. 将当前页面分成两页。所以第一页将包含名称 AB,第二页将包含 CD

假设 RDMS 选择选项 1(以减少索引碎片)。如果您插入一个名称为 C 和值 9 的新行,您将获得:

     Page 1              Page 2
| Name | Value |    | Name | Value |
|------|-------|    |------|-------|
|   A  |   1   |    |   C  |   9   |
|   B  |   2   |    |      |       |
|   C  |   3   |    |      |       |
|   D  |   4   |    |      |       |


可能Name 列中的 OrderBy 子句将返回以下内容:

| Name | Value |
|------|-------|
|   A  |   1   |
|   B  |   2   |
|   C  |   3   |
|   C  |   9   | -- Value 9 appears after because it was at another page
|   D  |   4   |


现在,假设 RDMS 选择选项 2(以提高具有许多心轴的存储系统中的插入性能)。如果您插入一个名称为 C 和值 9 的新行,您将获得:

     Page 1              Page 2
| Name | Value |    | Name | Value |
|------|-------|    |------|-------|
|   A  |   1   |    |   C  |   3   |
|   B  |   2   |    |   D  |   4   |
|   C  |   9   |    |      |       |
|      |       |    |      |       |


可能Name 列中的 OrderBy 子句将返回以下内容:

| Name | Value |
|------|-------|
|   A  |   1   |
|   B  |   2   |
|   C  |   9   |  -- Value 9 appears before because it was at the first page
|   C  |   3   | 
|   D  |   4   |


关于你的例子:

我相信您在问题中输入了错误的内容,因为您使用了items.OrderBy(i => i.rate).Skip(2).Take(2);,而第一个结果没有显示带有Rate = 2 的行。这是不可能的,因为Skip 将忽略前两行并且它们具有Rate = 1,因此您的输出必须显示带有Rate = 2 的行。

您已经用database 标记了您的问题,所以我相信您使用的是LINQ to SQL。在这种情况下,结果可能是不确定的,您可能会得到以下结果:

结果一:

[{"id":40, "description":"aaa", "rate":1},
 {"id":4, "description":"ccc", "rate":2}]

结果 2:

[{"id":1, "description":"bbb", "rate":1},
 {"id":4, "description":"ccc", "rate":2}]

如果您使用了items.OrderBy(i => i.rate).ThenBy(i => i.ID).Skip(2).Take(2);,那么唯一可能的结果是:

[{"id":40, "description":"aaa", "rate":1},
 {"id":4, "description":"ccc", "rate":2}]

【讨论】:

  • 值得注意的是,LINQ to objects 对 OrderBy 使用了稳定的排序,但是转换为 SQL 的 linq 查询将使用 ORDER BY 的数据源实现。
  • 感谢@RyanDansie,我已将您的注释添加到我的回答中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多