Linq to sql - 返回记录子集的最佳方式和记录总数

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

标签: linq linq-to-sql

我正在查看一些用于分页表的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命中。

这是最佳的,是否有更好的解决方案?

谢谢!

2 个答案:

答案 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();

不是选择所有内容然后对内存中的对象进行计数,而是通过查询执行计数,然后返回应该快得多的数字。

为了回应您的更新,我认为您不会有更好的方法。它最终归结为两个查询,一个用于获取记录总数,另一个用于获取其中的一部分。