使用 EntityFramework 获取有序范围的最有效方法是什么?

What is the most efficient way to obtain an ordered range using EntityFramework?

困境

我有一个大型数据集,我需要对其中的一部分执行复杂的计算。对于我的计算,我需要根据输入参数从一个大集合中提取 有序数据块

我的方法签名如下所示:

double Process(Entity e, DateTimeOffset? start, DateTimeOffset? end)

可能的解决方案

以下两种方法spring记住:

方法 1 - WHERE 子句

double result = 0d;
IEnumerable<Quote> items = from item in e.Items
                           where (!start.HasValue || item.Date >= start.Value)
                              && (!end.HasValue || item.Date <= end.Value)
                           orderby item.Date ascending
                           select item;
...
return result;

方法 2 - 跳过并拿走

double result = 0d;
IEnumerable<Item> items = e.Items.OrderBy(i => i.Date);
if (start.HasValue)
    items = items.SkipWhile(i => i.Date < start.Value);
if (end.HasValue)
    items = items.TakeWhile(i => i.Date <= end.Value);
...
return result;

问题

如果我只是把它们放在一起,我可能会选择 方法 1,但是我的数据集的大小和数据集集的大小都是太大而无法忽略轻微的效率损失,并且对生成的可枚举进行排序至关重要。

哪种方法生成的查询效率更高?还有我尚未考虑的更有效的方法吗?

所提供的任何解决方案都可以安全地假设 table 索引良好。

根据 link 你不能使用 SkipWhile 而不具体化查询,所以在 2. 情况下你具体化所有实体,然后计算结果。

在 1. 场景中,您可以让 sql 处理此查询并仅具体化必要的记录,因此这是更好的选择。

编辑:

我编写了示例数据,对数据库进行了查询:

  1. SELECT [项目 1].[Id] AS [Id], [项目 1].[添加日期] AS [添加日期], [Project1].[SendDate] AS [SendDate] 从 ( SELECT [Extent1].[Id] AS [Id], [Extent1].[AddedDate] AS [AddedDate], [Extent1].[SendDate] AS [SendDate]<br> FROM [dbo].[Alerts] AS [Extent1] WHERE ([Extent1].[AddedDate] >= @p__linq__0) AND ([Extent1].[AddedDate] <= @p__linq__1) ) 作为 [项目 1] 按 [Project1].[AddedDate] ASC

  2. 排序
  3. SELECT [Extent1].[Id] AS [Id], [Extent1].[AddedDate] AS [AddedDate], [Extent1].[SendDate] AS [SendDate] FROM [dbo].[Alerts] AS [Extent1] ORDER BY [Extent1].[AddedDate] ASC

我插入了 1 000 000 条记录,并在结果中编写了预期为 1 行的查询。在 1 个案例中,查询时间为 291 毫秒并立即实现。在第二种情况下,查询时间为 1065 毫秒,我不得不等待大约 10 秒才能实现结果;

不支持将

SkipWhile 翻译成 SQL。你需要放弃那个选项。

解决此问题的最佳方法是在用于 select 范围的字段上创建索引,然后发出 SARGable 查询。 where date >= start && date < end 是 SARGable 并且可以使用索引。

!start.HasValue || 不是一个好主意,因为这会破坏 SARGability。构建查询,这样就不需要了。例如:

if(start != null) query = query.Where(...);

使索引覆盖,您将获得最佳性能。没有需要处理的额外行。