Linq Skip& Take - 为什么要构建一个WHERE子句?

时间:2017-09-12 16:59:29

标签: c# sql linq linq-to-sql paging

我正在实施服务器端分页和排序,显然每个人都建议使用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();

你能帮我找一个替代解决方案吗?

3 个答案:

答案 0 :(得分:1)

您可以直接针对数据库运行查询并查看执行计划吗?你能比较在管理工作室运行它到应用程序端的时间吗?对我来说,该查询没有任何内在错误,您可能希望在SQL中使用row_number,以便最有效地完成此操作。基于查询,我唯一能想到的就是索引。如果VCalculationResult是一个大表并且没有在id上编入索引,那么你每次都必须进行表扫描而不是搜索。此外,如果ID不是唯一的,您必须查看添加其他列以涵盖查询。

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

答案 1 :(得分: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();

如果您使用的是具有ROW_NUMBER功能的T-SQL或类似的东西,您可以执行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();

答案 2 :(得分:0)

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