哪个方法执行得更好:.Any()vs .Count()> 0?
在System.Linq
命名空间中,我们现在可以扩展IEnumerable
的Any()
和Count()
扩展方法 。
我最近被告知,如果我想检查一个集合包含1个或多个项目,我应该使用.Count() > 0
扩展方法,而不是.Count() > 0
扩展方法,因为.Count()
扩展方法必须遍历所有的项目。
其次,一些集合有一个属性 (不是扩展方法),即Count
或Length
。 使用这些,而不是.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,但是在某些情况下Count
和Any
之间存在巨大的性能差异,不幸的是,似乎在任何情况下都不能坚持使用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的值。