Nota: ho scritto questa risposta quando Entity Framework 4 era effettivo. Il punto di questa risposta non era quello di entrare in banali test di prestazioni .Any()
vs. .Count()
Il punto era segnalare che EF è tutt'altro che perfetto. Le versioni più recenti sono migliori ... ma se hai una parte di codice che è lenta e utilizza EF, prova con TSQL diretto e confronta le prestazioni piuttosto che fare affidamento su ipotesi (che .Any()
è SEMPRE più veloce di .Count() > 0
).
Mentre sono d'accordo con la maggior parte delle risposte e dei commenti più votati, in particolare sui Any
segnali di punto l'intento dello sviluppatore è migliore diCount() > 0
, ho avuto una situazione in cui Count è più veloce per ordine di grandezza su SQL Server (EntityFramework 4).
Ecco una query con Any
quell'eccezione di timeout thew (su ~ 200.000 record):
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
versione eseguita in millisecondi:
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();
Ho bisogno di trovare un modo per vedere quale SQL esatto producono entrambi i LINQ, ma è ovvio che c'è un'enorme differenza di prestazioni tra Count
e Any
in alcuni casi, e sfortunatamente sembra che non si possa semplicemente attenersi Any
in tutti i casi.
EDIT: qui vengono generati gli SQL. Bellezze come puoi vedere;)
ANY
:
exec sp_executesql N'SELECT TOP (1)
[Project2]. [ContactId] AS [ContactId],
[Project2]. [CompanyId] AS [CompanyId],
[Project2]. [ContactName] AS [ContactName],
[Progetto2]. [Nome completo] AS [Nome completo],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Progetto2]. [Creato] AS [Creato]
FROM (SELEZIONA [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]
DA (SELEZIONA
[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]. [Contatti] AS [Extent1]
DOVE ([Extent1]. [CompanyId] = @ p__linq__0) AND ([Extent1]. [ContactStatusId] <= 3) E (NON ESISTE (SELEZIONA
1 AS [C1]
FROM [dbo]. [NewsletterLog] AS [Extent2]
WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])
))
) AS [Project2]
) AS [Project2]
DOVE [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],
[Progetto2]. [Nome completo] AS [Nome completo],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Progetto2]. [Creato] AS [Creato]
FROM (SELEZIONA [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]
DA (SELEZIONA
[Progetto1]. [ContactId] AS [ContactId],
[Project1]. [CompanyId] AS [CompanyId],
[Project1]. [ContactName] AS [ContactName],
[Progetto1]. [Nome completo] AS [Nome completo],
[Project1]. [ContactStatusId] AS [ContactStatusId],
[Progetto1]. [Creato] AS [Creato]
DA (SELEZIONA
[Extent1]. [ContactId] AS [ContactId],
[Extent1]. [CompanyId] AS [CompanyId],
[Extent1]. [ContactName] AS [ContactName],
[Extent1]. [FullName] AS [FullName],
[Extent1]. [ContactStatusId] AS [ContactStatusId],
[Extent1]. [Created] AS [Created],
(SELEZIONARE
COUNT (1) AS [A1]
FROM [dbo]. [NewsletterLog] AS [Extent2]
DOVE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
FROM [dbo]. [Contatti] AS [Extent1]
) AS [Progetto1]
WHERE ([Project1]. [CompanyId] = @ p__linq__0) AND ([Project1]. [ContactStatusId] <= 3) AND (0 = [Project1]. [C1])
) AS [Project2]
) AS [Project2]
DOVE [Project2]. [Row_number]> 99
ORDER BY [Project2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
Sembra che Where with EXISTS puro funzioni molto peggio del calcolo di Count e quindi di Where with Count == 0.
Fammi sapere se vedete qualche errore nelle mie scoperte. Ciò che può essere rimosso da tutto ciò indipendentemente dalla discussione Any vs Count è che qualsiasi LINQ più complesso è molto meglio quando riscritto come Stored Procedure;).