Linq Skip & Take - 为什么它构建 WHERE 子句?

问题描述 投票:0回答:3

我正在实现服务器端分页和排序,显然每个人都建议使用 SKIP 和 TAKE。 我必须等待 13 秒才能获得 25 行,然后我查了一下原因。在 SQL 中我得到以下查询:

 exec sp_executesql N'SELECT [t1].[Id], [t1].[Updated], [t1].[Updater], [t1].[ProductId], [t1].[AccountId], [t1].[CountryId], [t1].[CurrencyId], [t1].[Year], [t1].[Storage], [t1].[AuditLog]
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[Id], [t0].[Updated], [t0].[Updater], [t0].[ProductId], [t0].[AccountId], [t0].[CountryId], [t0].[CurrencyId], [t0].[Year], [t0].[Storage]) AS [ROW_NUMBER], [t0].[Id], [t0].[Updated], [t0].[Updater], [t0].[ProductId], [t0].[AccountId], [t0].[CountryId], [t0].[CurrencyId], [t0].[Year], [t0].[Storage], [t0].[AuditLog]
    FROM [MtrMain].[VCalculationResult] AS [t0]
    ) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]',N'@p0 int,@p1 int',@p0=5,@p1=15

还有 C# 代码:

context.CalculationResults
                        .Select(cre =>CalculationResult>.CopyFrom(cre))
                        .Skip(skip)
                        .Take(take)
                        .ToList();

您能帮我找到替代解决方案吗?

c# sql linq pagination linq-to-sql
3个回答
1
投票

您可以直接对数据库运行查询并查看执行计划吗?您能比较一下在 Management Studio 中运行它与在应用程序端运行它所需的时间吗?对我来说,该查询没有任何本质上的错误,您可能希望在 SQL 中使用 row_number 来最有效地完成此操作。基于查询我唯一能想到的是索引。如果 VCalculationResult 是一个大表并且未在 id 上建立索引,那么您必须每次都进行表扫描而不是查找。此外,如果 id 不唯一,您就必须考虑添加其他列来覆盖查询。

您还可以通过创建存储过程来获得一些性能提升,以便编译查询,而不必对每次传递的参数进行临时处理。当实体框架中的性能问题时,我有时会这样做。通常,对于像这样的大型数据集的分页搜索,我会创建一个存储过程并在搜索函数中运行执行语句,然后自己将结果映射到模型集合。在应用程序端需要更多的编码,但对于像这样的特定调用,它可能是值得的。


0
投票

如果您的表中已有有序连续字段(

Id
是连续的数字字段吗?)并且您不介意有不同的顺序,则可以在该字段上使用
Where
而不是
Skip 
/
Take
获取一页行。

如果您有一个不连续的有序字段,则可以运行一次查询来在内存中创建有序字段值及其行号的

List
,仅保存每个第 n 个值,然后再次使用
Where
查找属于该页面的行,假设您没有太多页面可以保存在内存中。

类似的东西

var index = context.CalculationResults.AsEnumerable().Select((cr,i) => new { cr.Id, i }).Where(g => g.i % take == 0).Select(g => g.Id).ToList();

(不幸的是,两个参数

Select
不适用于 LINQ to SQL。)

然后你可以使用

index
来获取页面:

var page = context.CalculationResults.Where(cr => cr.Id >= index[pageNum-1] && (pageNum < index.Count ? index.cr.Id < index[pageNum] : true)).ToList();

如果您使用T-SQL或类似的具有

ROW_NUMBER
功能的东西,您可以执行SQL查询来生成索引服务器端:

var index = context.ExecuteQuery<int>("SELECT ID FROM (SELECT ROW_NUMBER() OVER (ORDER BY ID) AS rn,ID FROM [MtrMain].[VCalculationResult]) AS t1 WHERE (t1.rn-1) % 10 = 0").ToList();

0
投票

谢谢大家。我尝试了不同的方法,当我开始构建存储过程时,我发现了查询缓慢的原因:我发现其中一个字段包含一个 XML 文件。选择除 XML 日志之外的所有字段(包括带有二进制文件的字段),使查询按照我需要的方式执行。

© www.soinside.com 2019 - 2024. All rights reserved.