linq - 哪种方法性能更好, Any( ) VS Count() >0?

  显示原文与译文双语对照的内容

System.Linq 命名空间中,我们现在可以将 IEnumerable 扩展到 Any()Count() 扩展方法。

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

其次,有些集合有一个属性 ( 不是一个扩展方法),它是 Count 或者 Length 。 最好使用那些,而不是 .Any() 或者 .Count()

年份/nae?

时间:

如果你开始使用 .Length 或者 .Count ( 如 ICollection<T>IList<T>List<T> 等) - 那么这将是最快的选项,因为它不需要通过 Any() 来检查 non-empty IEnumerable<T> 序列。

在某个iteration,通常只是 IEnumerable<T>,然后 Any() 将 能提高速度,因为它仅必须的妆容, 但是,请注意,Count()的LINQ-to-Objects实现确实检查 ICollection<T> ( 使用 .Count 作为优化) - 所以如果你的基础data-source是直接的列表/集合,就不会有很大的差别。 不要问我为什么不使用 non-generic ICollection 。。

当然,如果你已经使用linqto过滤等( Where 等), 你将有一个基于iterator-block序列,这 ICollection<T> 优化是无用的。

通常使用 IEnumerable<T>: 坚持 Any() ;-p

在 SQL Server 好吧我刚拍了情境中,计数通过magnitude.的顺序都非常快 下面是超时异常( 在 ~200.000 记录上)的查询:


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

在毫秒内执行的计数版本:


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

我需要找到一个方法来看看确切的sql都LINQs生产——但是很明显数之间有一个巨大的性能差异和在某些情况下,你可以不仅坚持任何在所有情况下。

编辑:这里生成了 sql 。 你可以看到的美丽;)

任意:

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]
FROM ( SELECT [Project2].[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], 
 [Extent1].[ContactStatusId] AS [ContactStatusId], 
 [Extent1].[Created] AS [Created]
 FROM [dbo].[Contact] AS [Extent1]
 WHERE ([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

计数:

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]
FROM ( SELECT [Project2].[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], 
 [Extent1].[ContactStatusId] AS [ContactStatusId], 
 [Extent1].[Created] AS [Created], 
 (SELECT 
 COUNT(1) AS [A1]
 FROM [dbo].[NewsletterLog] AS [Extent2]
 WHERE ([Extent1].[ContactId] = [Extent2].[ContactId]) AND (6 = [Extent2].[NewsletterLogTypeId])) AS [C1]
 FROM [dbo].[Contact] AS [Extent1]
 ) AS [Project1]
 WHERE ([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

看来,纯粹存在与工作的地方比计算计数然后做与计数 == 0.

如果你们发现我的发现中有错误,请告诉我。 什么可以的这一切不管任何更复杂的linq vs 计数的讨论是更好当写成存储过程;)。

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

当你运行 Table.Any(), 时,它将生成类似( 警告: 不要伤害大脑,试图理解它


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语句的末尾。

在这种情况下,.Any().Count()> 0 快。

...