Nota: Eu escrevi esta resposta quando o Entity Framework 4 era real. O objetivo desta resposta não era entrar em testes triviais .Any()
versus .Count()
testes de desempenho. O objetivo era sinalizar que a EF está longe de ser perfeita. Versões mais recentes são melhores ... mas se você tiver parte do código que é lenta e usa EF, teste com TSQL direto e compare o desempenho em vez de confiar em suposições (que .Any()
é SEMPRE mais rápida que .Count() > 0
).
Embora eu concorde com a maioria das respostas e comentários votados - especialmente sobre a intenção dos desenvolvedores deAny
sinais pontuais melhor do que -, tive uma situação em que Count é mais rápido por ordem de grandeza no SQL Server (EntityFramework 4).Count() > 0
Aqui está a consulta com Any
essa exceção de tempo limite (em ~ 200.000 registros):
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
versão executada em questão de milissegundos:
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();
Preciso encontrar uma maneira de ver o SQL exato que ambos os LINQs produzem - mas é óbvio que há uma enorme diferença de desempenho entre Count
e, Any
em alguns casos, e, infelizmente, parece que você não pode ficar Any
em todos os casos.
Edição: Aqui são gerados SQLs. Belezas como você pode ver;)
ANY
:
exec sp_executesql N'SELECT TOP (1)
[Projeto2]. [ContactId] AS [ContactId],
[Projeto2]. [CompanyId] AS [CompanyId],
[Projeto2]. [Nome do contato] AS [Nome do contato],
[Projeto2]. [Nome completo] AS [Nome completo],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Projeto2]. [Criado] AS [Criado]
FROM (SELECIONE [Projeto2]. [ID do contato] AS [ID do contato], [Projeto2]. [ID da empresa] AS [ID da empresa], [Projeto2]. [Nome do contato] AS [Nome do contato], [Projeto2]. [Nome completo] AS [Nome completo] , [Projeto2]. [ContactStatusId] AS [ContactStatusId], [Projeto2]. [Criado] AS [Criado], row_number () OVER (ORDER BY [Projeto2]. [ContactId] ASC) AS [row_number]
FROM (SELECIONAR
[Extent1]. [ContactId] AS [ContactId],
[Extensão1]. [CompanyId] AS [CompanyId],
[Extent1]. [ContactName] AS [ContactName],
[Extensão1]. [Nome completo] AS [Nome completo],
[Extent1]. [ContactStatusId] AS [ContactStatusId],
[Extensão1]. [Criado] AS [Criado]
FROM [dbo]. [Contato] AS [Extensão1]
WHERE ([Extent1]. [CompanyId] = @ p__linq__0) AND ([Extent1]. [ContactStatusId] <= 3) AND (NÃO EXISTE (SELECT
1 AS [C1]
FROM [dbo]. [NewsletterLog] AS [Extent2]
WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])
)))
) AS [Projeto2]
) AS [Projeto2]
ONDE [Projeto2]. [Número da linha]> 99
ORDER BY [Projeto2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
COUNT
:
exec sp_executesql N'SELECT TOP (1)
[Projeto2]. [ContactId] AS [ContactId],
[Projeto2]. [CompanyId] AS [CompanyId],
[Projeto2]. [Nome do contato] AS [Nome do contato],
[Projeto2]. [Nome completo] AS [Nome completo],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Projeto2]. [Criado] AS [Criado]
FROM (SELECIONE [Projeto2]. [ID do contato] AS [ID do contato], [Projeto2]. [ID da empresa] AS [ID da empresa], [Projeto2]. [Nome do contato] AS [Nome do contato], [Projeto2]. [Nome completo] AS [Nome completo] , [Projeto2]. [ContactStatusId] AS [ContactStatusId], [Projeto2]. [Criado] AS [Criado], row_number () OVER (ORDER BY [Projeto2]. [ContactId] ASC) AS [row_number]
FROM (SELECIONAR
[Project1]. [ContactId] AS [ContactId],
[Projeto1]. [CompanyId] AS [CompanyId],
[Projeto1]. [Nome do contato] AS [Nome do contato],
[Projeto1]. [Nome completo] AS [Nome completo],
[Project1]. [ContactStatusId] AS [ContactStatusId],
[Projeto1]. [Criado] AS [Criado]
FROM (SELECIONAR
[Extent1]. [ContactId] AS [ContactId],
[Extensão1]. [CompanyId] AS [CompanyId],
[Extent1]. [ContactName] AS [ContactName],
[Extensão1]. [Nome completo] AS [Nome completo],
[Extent1]. [ContactStatusId] AS [ContactStatusId],
[Extensão1]. [Criado] AS [Criado],
(SELECT
COUNT (1) AS [A1]
FROM [dbo]. [NewsletterLog] AS [Extent2]
WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
FROM [dbo]. [Contato] AS [Extensão1]
) AS [Projeto1]
WHERE ([Projeto1]. [CompanyId] = @ p__linq__0) AND ([Projeto1]. [ContactStatusId] <= 3) AND (0 = [Projeto1]. [C1])
) AS [Projeto2]
) AS [Projeto2]
ONDE [Projeto2]. [Número da linha]> 99
ORDER BY [Projeto2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
Parece que puro Where with EXISTS funciona muito pior do que calcular Count e depois fazer Where with Count == 0.
Deixe-me saber se vocês vêem algum erro nas minhas descobertas. O que se pode tirar disso tudo, independentemente da discussão Any vs Count, é que qualquer LINQ mais complexo fica muito melhor quando reescrito como Procedimento Armazenado;).