使用LINQ高效地对大数据集进行分页

3

当研究使用LINQ在C#中实现分页的最佳方法时,大多数建议如下:

// Execute the query
var query = db.Entity.Where(e => e.Something == something);

// Get the total num records
var total = query.Count();

// Page the results
var paged = query.Skip((pageNum - 1) * pageSize).Take(pageSize);

这似乎是一个常见的建议策略(简化版)。

对我来说,分页的主要目的是为了提高效率。如果我的表中包含1.2百万条记录,其中Something == something,我不想同时检索所有记录。相反,我想分页数据,尽可能地获取少量记录。但是使用这种方法,似乎这是一个无意义的点。

如果我理解正确,第一条语句仍然检索了1.2百万条记录,然后根据需要进行分页。

以这种方式进行分页实际上会提高性能吗?如果每次都要检索1.2百万条记录,除了明显的UI好处外,还有什么意义吗?

我的理解有误吗?有没有任何.NET大师可以给我关于LINQ、分页和处理大型数据集时的性能方面的课程?


@Mr.香港人:不会。在Linq中使用.Select不会导致枚举。 - spender
3个回答

8

第一条语句不会执行实际的SQL查询,它只构建你打算运行的查询的一部分。

只有当你调用query.Count()时,第一条语句才会被执行。

SELECT COUNT(*) FROM Table WHERE Something = something

query.Skip().Take() 中,也不会执行查询,只有在尝试枚举结果(在 paged 上执行 foreach 或调用 .ToList())时才会执行相应的 SQL 语句,仅检索该页的行(使用 ROW_NUMBER)。
如果在 SQL Profiler 中查看此操作,您将看到确切地执行了两个查询,并且在任何时间点都不会尝试检索完整表格。
当使用调试器时要小心,因为如果您在第一条语句之后逐步执行并尝试查看 query 的内容,那么将执行 SQL 查询。这可能是您误解的原因。

谢谢!你关于调试器的笔记非常有帮助——调试query是促使我提出这个问题的原因。感谢你的回答。 - Mark Miller

3
// Execute the query
var query = db.Entity.Where(e => e.Something == something);

提供给你的信息是,第一句话之后没有任何名称

// Get the total num records
var total = query.Count();

这个计数查询会被翻译成SQL,并且会调用数据库。由于生成的SQL语句类似于以下内容,所以此调用不会获取所有记录:

SELECT COUNT(*) FROM Entity where Something LIKE 'something'

对于最后一个查询,它也没有获取到所有的记录。该查询将被转换为SQL,并在数据库中运行分页操作。
也许你会发现这个问题有用:如何高效地实现分页

谢谢!+1。如果我能够给出两个勾选标记,我会这样做的,但是Equiso在调试器方面的注释更有帮助。感谢您的帮助! - Mark Miller

2
我认为Entity Framework可能会根据linq语句结构化SQL查询条件。(例如使用ROWNUMBER() OVER ...)。不过,我也可能是错的。我会运行SQL分析器并查看生成的查询内容。

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