哪个方法执行得更好:.Any()vs .Count()> 0?

System.Linq命名空间中,我们现在可以扩展IEnumerableAny()Count() 扩展方法

我最近被告知,如果我想检查一个集合包含1个或多个项目,我应该使用.Count() > 0扩展方法,而不是.Count() > 0扩展方法,因为.Count()扩展方法必须遍历所有的项目。

其次,一些集合有一个属性 (不是扩展方法),即CountLength 。 使用这些,而不是.Count().Count()会更好吗?

是/娜?

如果你的东西有一个.Count.Count (如ICollection<T>IList<T>List<T>等),那么这将是最快的select,因为它不需要通过Any()检查非空IEnumerable<T>序列所需的GetEnumerator() / MoveNext() / Dispose()序列。

对于IEnumerable<T> ,那么Any() 通常会更快,因为它只需要看一次迭代。 但是请注意, Count()的LINQ-to-Objects实现会检查ICollection<T> (使用.Count作为优化) – 所以如果你的底层数据源直接是一个列表/集合,那么就不会有巨大的差异。 不要问我为什么不使用非generics的ICollection

当然,如果你使用LINQ来过滤它(等等),你将有一个基于迭代器块的序列,所以这个ICollection<T>优化是没有用的。

一般用IEnumerable<T> :用Any() -p粘住

注意:对于较新版本的EntityFramework,此答案不再正确。 至less从6.1.1 .Any()已被修复生成更好的SQL,并比.Count() > 0更快,正如在本和Kamotesting显示。 如果您不得不使用较旧版本的EF,则此答案仍然有效。

虽然我同意最新的答案和评论 – 特别是在Any信号开发人员意图Count() > 0更好 – 我已经有了在SQL Server(EntityFramework 4)上数量级更快的情况。

这里是查询与Any超时例外(约200.000logging):

 con = db.Contacts. Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated && !a.NewsletterLogs.Any(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr) ).OrderBy(a => a.ContactId). Skip(position - 1). Take(1).FirstOrDefault(); 

Count版本以毫秒为单位执行:

 con = db.Contacts. Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated && a.NewsletterLogs.Count(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr) == 0 ).OrderBy(a => a.ContactId). Skip(position - 1). Take(1).FirstOrDefault(); 

我需要find一种方法来查看LINQ产生的确切的SQL,但是在某些情况下CountAny之间存在巨大的性能差异,不幸的是,似乎在任何情况下都不能坚持使用Any

编辑:这里是生成的SQL。 美女,你可以看到;)

ANY

 exec sp_executesql N'SELECT TOP(1) 
 [Project2]。[ContactId] AS [ContactId], 
 [Project2]。[CompanyId] AS [CompanyId], 
 [Project2]。[ContactName] AS [ContactName], 
 [Project2]。[FullName] AS [FullName], 
 [Project2]。[ContactStatusId] AS [ContactStatusId], 
 [Project2]。[Created] AS [Created]
 [ContactId] AS [ContactId],[Project2]。[CompanyId] AS [CompanyId],[Project2]。[ContactName] AS [ContactName],[Project2]。[FullName] AS [FullName] ,[Project2]。[ContactStatusId] AS [ContactStatusId],[Project2]。[Created] AS [Created],row_number()OVER(ORDER BY [Project2]。[ContactId] ASC)AS [row_number]
     FROM(SELECT 
         [Extent1]。[ContactId] AS [ContactId], 
         [Extent1]。[CompanyId] AS [CompanyId], 
         [Extent1]。[ContactName] AS [ContactName], 
         [Extent1]。[FullName] AS [FullName], 
         [联系人状态Id] 
         [Extent1]。[Created] AS [Created]
         FROM [dbo]。[联系人] AS [Extent1]
         ([Extent1]。[CompanyId] = @ p__linq__0)AND([Extent1]。[ContactStatusId] <= 3)AND(NOT EXISTS(SELECT 
             1 AS [C1]
             FROM [dbo]。[NewsletterLog] AS [Extent2]
             WHERE([Extent1]。[ContactId] = [Extent2]。[ContactId])AND(6 = [Extent2]。[NewsletterLogTypeId])
         ))
     )AS [Project2]
 )AS [Project2]
 WHERE [Project2]。[row_number]> 99
 ORDER BY [Project2]。[ContactId] ASC',N'@ p__linq__0 int',@ p__linq__0 = 4

COUNT

 exec sp_executesql N'SELECT TOP(1) 
 [Project2]。[ContactId] AS [ContactId], 
 [Project2]。[CompanyId] AS [CompanyId], 
 [Project2]。[ContactName] AS [ContactName], 
 [Project2]。[FullName] AS [FullName], 
 [Project2]。[ContactStatusId] AS [ContactStatusId], 
 [Project2]。[Created] AS [Created]
 [ContactId] AS [ContactId],[Project2]。[CompanyId] AS [CompanyId],[Project2]。[ContactName] AS [ContactName],[Project2]。[FullName] AS [FullName] ,[Project2]。[ContactStatusId] AS [ContactStatusId],[Project2]。[Created] AS [Created],row_number()OVER(ORDER BY [Project2]。[ContactId] ASC)AS [row_number]
     FROM(SELECT 
         [Project1]。[ContactId] AS [ContactId], 
         [Project1]。[CompanyId] AS [CompanyId], 
         [Project1]。[ContactName] AS [ContactName], 
         [Project1]。[FullName] AS [FullName], 
         [Project1]。[ContactStatusId] AS [ContactStatusId], 
         [Project1]。[Created] AS [Created]
         FROM(SELECT 
             [Extent1]。[ContactId] AS [ContactId], 
             [Extent1]。[CompanyId] AS [CompanyId], 
             [Extent1]。[ContactName] AS [ContactName], 
             [Extent1]。[FullName] AS [FullName], 
             [联系人状态Id] 
             [Extent1]。[Created] AS [Created], 
             (select 
                 COUNT(1)AS [A1]
                 FROM [dbo]。[NewsletterLog] AS [Extent2]
                 ([Extent1]。[ContactId] = [Extent2]。[ContactId])AND(6 = [Extent2]。[NewsletterLogTypeId]))AS [C1]
             FROM [dbo]。[联系人] AS [Extent1]
         )AS [Project1]
         ([Project1]。[CompanyId] = @ p__linq__0)AND([Project1]。[ContactStatusId] <= 3)AND(0 = [Project1]。[C1])
     )AS [Project2]
 )AS [Project2]
 WHERE [Project2]。[row_number]> 99
 ORDER BY [Project2]。[ContactId] ASC',N'@ p__linq__0 int',@ p__linq__0 = 4

看来,纯粹的地方与EXISTS比计算计数,然后做在哪里计数== 0更糟糕。

让我知道你们是否在我的发现中看到了一些错误。 无论Any vs Count讨论如何,所有这一切都可以被解决,任何更复杂的LINQ在重写为Stored Procedure时都会更好);)。

由于这是相当热门的话题,答案不同,我不得不重新审视一下问题。

testing环境: EF 6.1.3,SQL Server,300klogging

表模型

 class TestTable { [Key] public int Id { get; set; } public string Name { get; set; } public string Surname { get; set; } } 

testing代码:

 class Program { static void Main() { using (var context = new TestContext()) { context.Database.Log = Console.WriteLine; context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000); context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000); context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000); context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000); Console.ReadLine(); } } } 

结果:

任何()〜3ms

Count()〜230ms为第一个查询,〜400ms为秒

备注:

对于我的情况,EF没有像他在post中提到的@Ben那样生成SQL。

编辑:它被固定在EF版本6.1.1。 而这个答案不再是实际的

对于SQL Server和EF4-6,Count()比Any()执行速度快两倍。

当你运行Table.Any()时,它会产生类似( alert:不要伤害大脑,试图理解它

 SELECT CASE WHEN ( EXISTS (SELECT 1 AS [C1] FROM [Table] AS [Extent1] )) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 1 AS [C1] FROM [Table] AS [Extent2] )) THEN cast(0 as bit) END AS [C1] FROM ( SELECT 1 AS X ) AS [SingleRowTable1] 

这需要对条件进行2次扫描。

我不喜欢写Count() > 0因为它隐藏了我的意图。 我更喜欢为此使用自定义谓词:

 public static class QueryExtensions { public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) { return source.Count(predicate) > 0; } } 

那么, .Count()扩展方法将不会使用.Count属性,但是我会假设你不会使用.Count()方法作为简单的集合,而是在具有过滤标准的LINQ语句的末尾等等

在这种情况下, .Count() > 0会比.Count() > 0更快。

这取决于数据集有多大,性能要求是什么?

如果没有什么大问题的话,那就使用最可读的forms,这对我自己来说是可行的,因为它比较短而且可读,而不是一个等式。

你可以做一个简单的testing来解决这个问题:

进行任何查询,然后:

  var timeCount = new Stopwatch(); timeCount.Start(); if (query.Count > 0) { } timeCount.Stop(); var testCount = timeCount.Elapsed; var timeAny = new Stopwatch(); timeAny.Start(); if (query.Any()) { } timeAny.Stop(); var testAny = timeAny.Elapsed; 

检查testCount和testAny的值。