【问题标题】:C# Linq query execution orderC# Linq 查询执行顺序
【发布时间】:2021-10-02 08:48:16
【问题描述】:

考虑以下方法:

public IEnumerable<Owner> GetOwners(OwnerParameters ownerParameters)
{
    return FindAll()
        .OrderBy(on => on.Name)
        .Skip((ownerParameters.PageNumber - 1) * ownerParameters.PageSize)
        .Take(ownerParameters.PageSize)
        .ToList();
}

其中FindAll() 是返回IQueryable&lt;Owner&gt; 的存储库模式方法。在.Skip().Take() 方法之前有.OrderBy() 是否意味着Owner 数据表中的所有元素都将被检索和排序,或者Linq 是否考虑到.Skip().Take() 方法可能会缩小删除所需的Owner 元素,只有在检索到这些元素后才会进行排序?

编辑:分析器日志:

SELECT XXX
FROM [Owners] AS [a]
ORDER BY [a].[Name]
OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY',N'@__p_0 int,@__p_1 int',@__p_0=0,@__p_1=10

【问题讨论】:

  • 对于因“需要更多细节或清晰度”而投票决定关闭此内容的人,您的意思究竟是什么?

标签: c# linq entity-framework-core


【解决方案1】:

最终,这取决于FindAll() 做了什么以及它返回了什么:

  • 如果它返回IEnumerable&lt;T&gt;,那么它使用的是LINQ-to-Objects,大部分只是按照字面意思执行;如果您对页面进行排序,则 it 对页面进行排序;如果你分页然后排序,那么 it 分页然后排序
  • 但是,如果它返回 IQueryable&lt;T&gt;,则查询正在组合 - 并且仅在 ToList() 中实际执行,此时提供程序模型有机会检查您的查询树并尽可能构建最合适的实现,这通常意味着:编写包含ORDER BY 和一些适用于特定RDBMS 的分页提示的SQL 查询;如果您的代码分页然后排序(这是...不寻常的),那么我会 expect 大多数提供者 either 编写某种可怕的子查询来尝试描述它, 或者只是厌恶地抛出一个异常(也许是NotSupportedException

【讨论】:

  • 肯定是IQueryable&lt;Owner&gt;。 RDBMS 是 SQL Server。那么这是否意味着,不会检索整个Owner 表,而只会检索.Take().Skip() 请求的部分,并且之后才排序(排序)?
  • @astralmaster 我绝对希望发出一个在数据库服务器上进行排序和分页的查询,只返回相关页面;然而!如果不确定:使用任何可用的分析和监控工具
  • @MarcGravell:我不同意它“仅在之后订购”。数据库需要先应用排序才能使用分页,否则返回的行是任意的。因此,也许我误解了 OP 的评论或您的回答,或者您忽略了它。
  • @TimSchmelter 哦,是的,我的解释/措辞很差;我说“是”的意思是“否” :) after the yes 的话似乎很清楚,但是;我删除了“是”(其余不变)
  • 在 SQL 服务器中,它会生成如下代码: SELECT ... FROM ... ORDER BY ... OFFSET x ROWS FETCH NEXT y ROWS ONLY,它在服务器端进行排序并仅获取需要行,但是该查询在 MS SQL 服务器中很慢。自定义查询要好得多。
【解决方案2】:

不会检索所有记录。根据您的后端,将生成一个查询,按名称排序,跳过 N 行并获取接下来的 M 行。仅检索查询结果,由 .ToList() 触发。 即:在 MS SQL 服务器中,查询可能如下所示:

Select top(M) row_number() over (order by Name) as RowNo, * 
from myTable
where RowNo > N

这种类型的查询不是很有效,但那是另一回事,您可以创建自定义解决方法。

编辑:我记得 MS SQL 代码生成错误。那是:

SELECT ...
FROM ...
ORDER BY ...
OFFSET x ROWS FETCH NEXT y ROWS ONLY

那个也很慢。如果您考虑速度,请编写自己的 SQL 并使用 Linq 发送。基本上你所做的是保留 lastRetrieved 值并将其设置为参数:

select top(NTake) ... from ... 
order by ... 
where orderedByValue > @lastRetrievedValue 

(您可以在 Linq 中发送原始 SQL 查询)

【讨论】:

  • 谢谢。您能否详细说明为什么它不会很有效?
  • @astralmaster,创建 row_number() ...然后获取 top (N) 是一个缓慢的操作(不是 TOP N,但 row_number() 部分很慢)。如果您有大量数据并且需要获取很多页面,这会变得很慢。相反,一个自定义查询,您保留最后检索到的名称客户端并要求大于该名称的名称要快得多(假设您在名称上有一个索引)。实际上有一篇关于这个的帖子,这种自定义查询方法要快得多。如果我能找到那个帖子,我会添加链接。
  • 那么你会说,使用 Linq 和 Entity Framework 会为这种确切的情况产生明显较慢的查询,而宁愿自己构建一个?
  • @astralmaster,是的。但快、慢……都是相对于它的用户而言的。对我来说很慢的东西,在小型数据集中可能对您来说是可以接受的(那时自定义查询增益可以忽略不计)。我仍然找不到那个帖子 :( - 这是我的帖子。
  • @astralmaster,我终于找到了。 stackoverflow.com/questions/34531818/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2014-07-30
  • 2020-04-11
相关资源
最近更新 更多