【问题标题】:What is the most efficient way to obtain an ordered range using EntityFramework?使用 EntityFramework 获取有序范围的最有效方法是什么?
【发布时间】:2015-06-26 21:55:02
【问题描述】:

困境

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

我的方法签名如下所示:

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

可能的解决方案

想到以下两种方法:

方法 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,但是我的数据集的大小和数据集的大小都太大而无法忽略效率损失,并且对生成的可枚举项进行排序至关重要。

哪种方法会产生更有效的查询?还有没有更有效的方法我还没有考虑?

提出的任何解决方案都可以安全地假设该表已被良好索引。

【问题讨论】:

  • 您应该使用 IQueryable 来确保生成 SQL 并且数据集没有加载到内存中。您可以尝试使用 SQL 分析器查看生成的 SQL 并在您的服务器上运行它以查看执行时间。
  • IQueryable 很好。我应该比那更清楚。基于此修复了问题。
  • Aaa 然后我回滚,意识到 IQueryable 不能与 orderby 配合使用。不过,我确实确认它没有提前评估。
  • 因为您在 DateTime 上使用 .Date,这是本机 C# 代码,需要加载到 CLR 中,因此永远不会在 SQL 中运行。您应该使用 EntityFunctions 在 OrderBy msdn.microsoft.com/en-us/library/… 中创建一个没有时间的日期
  • 其实我不是。您正在查看的是假设类ItemDate 属性。它是DateTimeOffset 类型,正如方法签名所暗示的那样。 DateTimeOffset 将被 SQL Server 巧妙地处理。

标签: c# linq entity-framework entity-framework-6


【解决方案1】:

SkipWhile 不支持转换为 SQL。您需要放弃该选项。

解决此问题的最佳方法是在您用于范围选择的字段上创建一个索引,然后发出一个 SARGable 查询。 where date &gt;= start &amp;&amp; date &lt; end 是 SARGable 并且可以使用索引。

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

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

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

【讨论】:

  • 第一部分是我正在寻找的部分,谢谢:)。在我的实际代码中,我拆分了 .HasValue 逻辑,但在我寻求简洁写下问题的过程中,我看起来有点傻。
【解决方案2】:

根据link,您不能在不实现查询的情况下使用 SkipWhile,因此在 2. 情况下,您要实现所有实体,然后计算结果。

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

编辑:

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

  1. SELECT [Project1].[Id] AS [Id], [Project1].[AddedDate] AS [AddedDate], [Project1].[SendDate] AS [SendDate] FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[AddedDate] AS [AddedDate], [Extent1].[SendDate] AS [SendDate]
    FROM [dbo].[Alerts] AS [Extent1] WHERE ([Extent1].[AddedDate] >= @p__linq__0) AND ([Extent1].[AddedDate] <= @p__linq__1) ) AS [Project1] ORDER BY [Project1].[AddedDate] ASC

  2. 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 秒才能实现结果;

【讨论】:

  • 为努力和生成的查询点赞。我相信这将有助于理解:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
  • 2013-01-08
  • 1970-01-01
  • 1970-01-01
  • 2017-06-30
  • 2015-02-21
  • 2018-08-07
相关资源
最近更新 更多