使用 LINQ 高效分页大型数据集

Efficiently paging large data sets with LINQ

在寻找在 C# 中实现分页的最佳方法(使用 LINQ)时,大多数建议都是这样的:

// 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);

这似乎是通常建议的策略(已简化)。

对我来说,分页的主要目的是为了效率。如果我的 table 包含 120 万条记录,其中 Something == something,我不想同时检索所有这些记录。相反,我想分页数据,抓取尽可能少的记录。但是使用这种方法,似乎这是一个有争议的问题。

如果我没理解错的话,第一个语句仍然是检索120万条记录,然后它正在根据需要进行分页。

这样的分页真的能提高性能吗?如果每次都要检索 120 万条记录,那还有什么意义(除了显而易见的 UI 好处)?

我是不是误会了?有没有 .NET 专家可以给我上 LINQ、分页和性能(处理大型数据集时)的课程?

我相信 Entity Framework 可以根据 linq 语句用适当的条件构造 SQL 查询。 (例如,使用 ROWNUMBER() OVER ...)。

不过,我在这一点上可能是错的。我会 运行 SQL 探查器并查看生成的查询是什么样的。

第一条语句不执行实际的 SQL 查询,它只构建您打算 运行.

的部分查询

当你调用query.Count()时,第一个会被执行

SELECT COUNT(*) FROM Table WHERE Something = something

On query.Skip().Take() 也不会执行查询,只有当您尝试枚举结果时(在 paged 上执行 foreach 或对其调用 .ToList())它将执行适当的 SQL 语句,仅检索页面的行(使用 ROW_NUMBER)。

如果在 SQL 探查器中观看此内容,您会看到恰好执行了两个查询,并且在任何时候它都不会尝试检索完整的 table.


使用调试器时要小心,因为如果您在第一个语句之后单步执行并尝试查看将执行 SQL 查询的 query 的内容。也许这就是你误解的根源。

// 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,数据库中的分页运行

也许您会发现这个问题很有用:efficient way to implement paging