Nota: escribí esta respuesta cuando Entity Framework 4 era real. El objetivo de esta respuesta era no entrar en pruebas triviales .Any()
versus de .Count()
rendimiento. El punto era señalar que EF está lejos de ser perfecto. Las versiones más nuevas son mejores ... pero si tiene una parte del código que es lenta y usa EF, pruebe con TSQL directo y compare el rendimiento en lugar de confiar en suposiciones (eso .Any()
es SIEMPRE más rápido que .Count() > 0
).
Si bien estoy de acuerdo con la mayoría de las respuestas y comentarios votados, especialmente sobre el punto, las Any
señales del desarrollador son mejoresCount() > 0
, he tenido una situación en la que Count es más rápido por orden de magnitud en SQL Server (EntityFramework 4).
Aquí hay una consulta con Any
esa excepción de tiempo de espera (en ~ 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
versión ejecutada en cuestión de milisegundos:
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();
Necesito encontrar una manera de ver qué SQL exacto producen ambos LINQ, pero es obvio que hay una gran diferencia de rendimiento entre Count
y Any
en algunos casos, y desafortunadamente parece que no puede quedarse Any
en todos los casos.
EDITAR: Aquí se generan los SQL. Bellezas como puedes ver;)
ANY
:
exec sp_executesql N'SELECT TOP (1)
[Proyecto2]. [ContactId] AS [ContactId],
[Proyecto2]. [CompanyId] AS [CompanyId],
[Proyecto2]. [ContactName] AS [ContactName],
[Proyecto2]. [Nombre completo] AS [Nombre completo],
[Proyecto2]. [ContactStatusId] AS [ContactStatusId],
[Proyecto2]. [Creado] AS [Creado]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Proyecto2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Creado] AS [Creado], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
DESDE (SELECCIONAR
[Extensión1]. [ContactId] AS [ContactId],
[Extensión1]. [CompanyId] AS [CompanyId],
[Extensión1]. [ContactName] AS [ContactName],
[Extensión1]. [Nombre completo] AS [Nombre completo],
[Extensión1]. [ContactStatusId] AS [ContactStatusId],
[Extensión1]. [Creado] AS [Creado]
FROM [dbo]. [Contacto] AS [Extent1]
DONDE ([Extent1]. [CompanyId] = @ p__linq__0) Y ([Extent1]. [ContactStatusId] <= 3) Y (NO EXISTE (SELECCIONE
1 AS [C1]
DE [dbo]. [NewsletterLog] AS [Extent2]
WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])
))
) AS [Proyecto2]
) AS [Proyecto2]
WHERE [Project2]. [Row_number]> 99
ORDER BY [Proyecto2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
COUNT
:
exec sp_executesql N'SELECT TOP (1)
[Proyecto2]. [ContactId] AS [ContactId],
[Proyecto2]. [CompanyId] AS [CompanyId],
[Proyecto2]. [ContactName] AS [ContactName],
[Proyecto2]. [Nombre completo] AS [Nombre completo],
[Proyecto2]. [ContactStatusId] AS [ContactStatusId],
[Proyecto2]. [Creado] AS [Creado]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Proyecto2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Creado] AS [Creado], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
DESDE (SELECCIONAR
[Proyecto1]. [ContactId] AS [ContactId],
[Proyecto1]. [CompanyId] AS [CompanyId],
[Proyecto1]. [ContactName] AS [ContactName],
[Proyecto1]. [Nombre completo] AS [Nombre completo],
[Proyecto1]. [ContactStatusId] AS [ContactStatusId],
[Proyecto1]. [Creado] AS [Creado]
DESDE (SELECCIONAR
[Extensión1]. [ContactId] AS [ContactId],
[Extensión1]. [CompanyId] AS [CompanyId],
[Extensión1]. [ContactName] AS [ContactName],
[Extensión1]. [Nombre completo] AS [Nombre completo],
[Extensión1]. [ContactStatusId] AS [ContactStatusId],
[Extensión1]. [Creado] AS [Creado],
(SELECCIONE
CONTAR (1) COMO [A1]
DE [dbo]. [NewsletterLog] AS [Extent2]
WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
FROM [dbo]. [Contacto] AS [Extent1]
) AS [Proyecto1]
WHERE ([Project1]. [CompanyId] = @ p__linq__0) AND ([Project1]. [ContactStatusId] <= 3) AND (0 = [Project1]. [C1])
) AS [Proyecto2]
) AS [Proyecto2]
WHERE [Project2]. [Row_number]> 99
ORDER BY [Proyecto2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
Parece que puro Where with EXISTS funciona mucho peor que calcular Count y luego hacer Where with Count == 0.
Avíseme si ustedes ven algún error en mis hallazgos. Lo que se puede sacar de todo esto, independientemente de la discusión Any vs Count es que cualquier LINQ más complejo está mucho mejor cuando se reescribe como Procedimiento almacenado;).