我在整个应用程序中使用IQueryable<T>
接口,并推迟在DB上执行SQL,直到像.ToList()
我有时需要查找某些列表的计数 - 而不需要使用列表中的数据进行计数。根据我的SQL经验,我知道SQL COUNT()的工作量远远少于返回所有行的等效SELECT语句。
所以我的问题是:数据库从IQueryable<T>
的{{1}}方法返回计数,而不是将Count()
渲染到列表并调用列表IQueryable<T>
方法?
我怀疑它会给Count()
激活SELECT sql然后在单独的查询中计算行数。我希望ToList()
上的Count()
只是简单地为sql COUNT()查询呈现sql。但我不确定。你知道吗?
答案 0 :(得分:21)
调用ToList()
将返回包含所有数据的真实List<T>
,这意味着获取所有数据。不好。
调用Count()
确实应该使SQL在数据库端进行计数。好多了。
然而,检查此问题的最简单方法是启用数据上下文中的日志记录(或任何与您的特定提供程序等效的内容)并查看实际发送的查询。
答案 1 :(得分:-1)
我不确定它是否是一个硬性规则,但是你添加到Iqueryable的linq方法将被添加到linq表达式树中 - 除非它们是实际导致树被评估的方法之一(如ToList和Single等)。 对于LinqToSql,您将知道它是否无法将某些内容转换为SQL语句,因为您将获得一个运行时异常,指出该方法不受支持。
例如
var something = dbContext.SomeTable
.Select(c => c.col1 == "foo")
.Distinct()
.ToList()
.Count()
在上面,Select()和Distinct()包含在传递给服务器的sql查询中,因为它们被添加到Iqueryable中。 Count()只是作用于sql查询返回的列表。所以你不想这样做: - )
在你的情况下,Count()肯定会比Select()更快,因为生成的sql语句确实会包含计数,所以服务器只需要返回一个数字而不是一个行列表。
答案 2 :(得分:-1)
如果您正在使用SQL Server,Count()仍然非常昂贵,因为它会导致表扫描(或索引扫描,请参阅主要答案的注释)。并且,默认情况下,Linq不使用读取未注册的隔离级别,这会因锁定而使事情变得更糟。
如果您可以将结果视为脏结果和行总数的近似值,则以下代码将比使用Count()快得多。根据我的经验,此代码返回的值很少与行的真实数量不同。
/// <summary>A very fast method for counting rows in a table.</summary>
public static long FastRowCount(DataContext context, string tableName)
{
const string template = "SELECT rowcnt FROM sys.sysindexes WHERE id = OBJECT_ID('{0}') AND indid < 2";
string query = string.Format(template, tableName);
return context.ExecuteQuery<long>(query).Single();
}