我正在查看一些用于分页表的linq to SQL代码。在其中,它需要返回一组记录,以及数据库中的记录总数。代码如下所示:
var query = (from p in MyTable select new {p.HostCable, p.PatchingSet});
int total = query.ToList().Count;
query = query.Skip(5).Take(10);
我想稍微了解执行时会发生什么,我看到发生了2次查询 - 一次是从db获取所有行,另一次是获取子集。毋庸置疑,获取所有记录的性能影响并不好。我猜“ToList”强制执行查询,然后Count方法针对整个List运行。
在重新分解语句以提高效率时 - 这是我的改进版本:
int total = MyTable.Count();
var query = (from p in MyTable select new {p.HostCable, p.PatchingSet}).Skip(5).Take(10);
这会导致SQL命中“Select count ..”,然后是实际选择记录的SQL命中。
这是最佳的,是否有更好的解决方案?
谢谢!
答案 0 :(得分:2)
从我所看到的情况来看,如果没有一点黑客攻击,这在LINQ to SQL中是不可能的。
我发现这种方法有效。此方法的快速摘要:我将IQueryable
对象转换为命令对象,并修改命令文本以包括结果集中的总计数。原始LINQ to SQL转换器SQL查询使用ROW_NUMBER() OVER()
语法来分页行,我只需添加COUNT(*) OVER()
即可获得总计数。
将此方法添加到DataContext类。
public IEnumerable<TWithTotal> ExecutePagedQuery<T, TWithTotal>(IQueryable<T> query, int pageSize, int pageNumber, out int count)
where TWithTotal : IWithTotal
{
var cmd = this.GetCommand(query.Skip(pageSize * pageNumber).Take(pageSize));
var commandText = cmd.CommandText.Replace("SELECT ROW_NUMBER() OVER", "SELECT COUNT(*) OVER() AS TOTALROWS, ROW_NUMBER() OVER");
commandText = "SELECT TOTALROWS AS TotalCount," + commandText.Remove(0, 6);
cmd.CommandText = commandText;
var reader = cmd.ExecuteReader();
var list = this.Translate<TWithTotal>(reader).ToList();
if (list.Count > 0)
count = list[0].TotalCount;
else
count = 0;
return list;
}
您必须创建一个新类,其中包含实现IWithTotal
接口的原始对象的所有属性。
UPDATE:您无法在LINQ to SQL中混合使用映射和未映射的列。
public interface IWithTotal
{
int TotalCount { get; set; }
}
public class Project : IWithTotal
{
public int TotalCount { get; set; }
public int ProjectID { get; set; }
public string Name { get; set; }
}
DataContext.Translate
有一些要求,因此如果您遇到任何问题,请确保您的查询满足这些要求。
根据查询的复杂程度,您应该能够看到性能的提升。以下是我使用计数查询,分页选择查询和带有计数查询的分页选择进行的测试的指标。百分比是相对于批次的查询成本。
Count - 6%
Select with paging - 47%
Select with paging + Count - 48%
答案 1 :(得分:1)
int total = query.Count();
不是选择所有内容然后对内存中的对象进行计数,而是通过查询执行计数,然后返回应该快得多的数字。
为了回应您的更新,我认为您不会有更好的方法。它最终归结为两个查询,一个用于获取记录总数,另一个用于获取其中的一部分。