Linq to entities使用.Take()方法非常缓慢

6

我有一个包含20万个记录的表格,我正在使用.Take()方法获取其中前10个记录,但需要大约10秒钟才能获取到数据。

我的问题是: .Take()方法是否会从数据库中获取所有数据,然后在客户端过滤出前10条记录?

以下是我的代码:

mylist = (from mytable in db.spdata().OrderByDescending(f => f.Weight)
                                    group feed by mytable.id into g
                                    select g.FirstOrDefault()).Take(10).ToList();

spdata() 是从存储过程中导入的函数。

谢谢。


8
如果你想知道它在数据库中运行了什么,你应该连接一个分析器到你的数据库并检查日志,这是发现答案的最好方式。 - Chris
2
如果spdata()返回200,000,那么它将在客户端执行。 - Pablo Romeo
除了Chris的方法,您还可以尝试使用Linqpad,并在编写查询后切换到SQL视图以查看生成的SQL,或者使用断点(使用此方法https://dev59.com/P3M_5IYBdhLWcg3wWRyV)来查看程序执行时生成的内容。 - Aaron Newton
4个回答

9
存储过程可能会向客户端返回大量数据,这样非常慢。您不能将查询远程到存储过程中。可以使用视图或表值函数来实现这一点。
无法在查询中使用存储过程,只能单独执行它。
您的意图可能是在服务器上执行Take(10)。为了使其工作,您需要切换到内联查询、视图或 TVF。

7
扩展方法Take并不会从数据库中获取所有结果。这不是Take的工作方式。
然而,你的db.spdata()调用可能会获取所有行。

1

我不是100%确定,但我记得当您使用EF DataContext调用SP时,会获得IEnumerable结果...

有几种方法可以优化性能:

  • 将搜索条件作为SP参数传递,并在存储过程中进行过滤。

或者,如果您在SP中有一个非常简单的查询,其中没有声明任何变量,只是连接一些表,则可以:

  • 创建一个索引视图,指定所需的查询并在其上调用Take方法。
    这将给您什么?您可以映射到创建的视图,EF现在将返回一个IQueryable结果而不是IEnumerable。这将优化sql命令,而不是接收所有数据,然后获取您需要的10个元素,将形成一个仅检索10个元素的sql命令。

我还建议您查看IEnumerable与IQueryable之间的区别。


0

这是因为您在分组之前对数据进行了排序,而在 SQL 中无法实现。

您应该使用聚合函数来获取每个组中的最高权重,然后对权重进行排序以获取前十个最大值:

mylist = (
  from mytable in db.spdata()
  group feed by mytable.id into g
  select g.Max(f => f.Weight)
).OrderByDescending(w => w).Take(10).ToList();

出现错误:"无法在此范围内声明名为'g'的局部变量,因为它会给已经用于表示其他内容的'parent or current'范围中的'g'赋予不同的含义"。 - Alex
@AliIssa:没错,那里必须是一个不同的变量。我已经改了它。 - Guffa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接