在System.Linq
命名空间中,咱们如今能够将IEnumerable
扩展为具备Any()
和Count()
扩展方法 。 sql
最近,我被告知,若是我要检查一个集合包含在它里面1个或多个项目,我应该使用.Any()
而不是扩展方法.Count() > 0
扩展方法,由于.Count()
扩展方法必须遍历全部项目。 框架
其次,某些集合具备Count
或Length
属性 (不是扩展方法)。 它会更好利用这些,而不是.Any()
或.Count()
性能
是/否? 测试
注意:当实体框架4实际存在时,我写了这个答案。 这个答案的要点是不要进入微不足道.Any()
VS .Count()
性能测试。 关键是要代表EF远非完美。 新版本的比较好...但若是你的代码的一部分这是缓慢的,它采用EF,测试直接TSQL和性能进行比较,而不是依赖于假设(即.Any()
老是比快.Count() > 0
) 。 优化
尽管我赞成大多数投票同意的答案和评论-特别是在Any
点上, Any
信号开发人员的意图都比Count() > 0
-但我遇到的状况是,在SQL Server上,Count的数量级更快(EntityFramework 4)。 this
这是带有Any
that thew timeout异常(约200.000条记录)的查询: spa
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
以毫秒为单位的版本: code
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();
我须要找到一种方法来查看LINQ产生的确切SQL,但很明显,在某些状况下Count
和Any
之间存在巨大的性能差别,不幸的是,您彷佛不能在全部状况下都坚持使用Any
。 ip
编辑:这是生成的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] 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
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] 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
彷佛使用EXISTS进行纯Where运算要比计算Count而后执行Count == 0运算要差得多。
让我知道大家是否发现个人发现有误。 无论“任何与计数”的讨论如何,全部这一切均可以排除的是,将更复杂的LINQ重写为存储过程时会更好;)。
编辑:在EF版本6.1.1中已修复。 并且这个答案再也不实际
对于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; } }
这取决于数据集的大小以及您对性能的要求是什么?
若是没什么大不了的,请使用最易读的形式,对我本身来讲是任何形式,由于它更短,更易读,而不是方程式。
因为这是一个很是受欢迎的话题,而且答案各不相同,所以我不得不从新审视这个问题。
测试环境: EF 6.1.3,SQL Server,30万条记录
桌子型号 :
class TestTable { [Key] public int Id { get; set; } public string Name { get; set; } public string Surname { get; set; } }
测试代码:
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(); } } }
结果:
Any()〜3毫秒
Count()〜230ms用于第一次查询,〜400ms用于第二次查询
备注:
就我而言,EF并无像他的帖子中提到的@Ben那样生成SQL。
若是你开始的东西,有一个.Length
或.Count
(如ICollection<T>
IList<T>
List<T>
等) -那么这将是最快的选项,由于它并不须要经过Any()
所需的GetEnumerator()
/ MoveNext()
/ Dispose()
序列来检查非空IEnumerable<T>
序列。
仅对于IEnumerable<T>
,而后Any()
一般会更快,由于它只须要查看一次迭代便可。 可是,请注意, Count()
的LINQ-to-Objects实现确实检查了ICollection<T>
(使用.Count
做为优化)-所以,若是您的基础数据源直接是列表/集合,则不会巨大的差别。 不要问我为何不使用非通用ICollection
...
固然,若是您使用LINQ对其进行过滤( Where
等),则将具备基于迭代器块的序列,所以此ICollection<T>
优化是无用的。
一般使用IEnumerable<T>
:坚持使用Any()
;-p