Примечание. Я написал этот ответ, когда еще существовала Entity Framework 4. Суть этого ответа заключалась не в том, чтобы вдаваться в тривиальное тестирование производительности .Any() vs .Count(). Дело было в том, чтобы сигнализировать о том, что EF далек от совершенства. Более новые версии лучше ... но если у вас есть часть кода, который медленный и использует EF, протестируйте с прямым TSQL и сравните производительность, а не полагайтесь на предположения (что .Any() ВСЕГДА быстрее, чем .Count() > 0).
Хотя я согласен с наиболее одобренными ответами и комментариями - особенно в отношении того, что Any сигнализирует о намерении разработчика лучше, чем Count() > 0, - у меня была ситуация, в которой Count на SQL Server на порядок быстрее (EntityFramework 4).
Вот запрос с Any, что исключение тайм-аута w (для ~ 200000 записей):
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();
Мне нужно найти способ увидеть, какой именно SQL выдают оба LINQ, но очевидно, что в некоторых случаях существует огромная разница в производительности между 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]
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
Кажется, что чистый Where с EXISTS работает намного хуже, чем вычисление Count, а затем выполнение Where с Count == 0.
Дайте мне знать, если вы заметите ошибку в моих выводах. Что можно извлечь из всего этого, независимо от обсуждения Any vs Count, так это то, что любой более сложный LINQ будет намного лучше, если его переписать как хранимую процедуру;).
person
nikib3ro
schedule
14.06.2012