Uwaga: napisałem tę odpowiedź, gdy Entity Framework 4 był aktualny. Celem tej odpowiedzi nie było przejście do testów trywialnych .Any()
vs .Count()
wydajnościowych. Chodziło o to, aby zasygnalizować, że EF jest daleki od ideału. Nowsze wersje są lepsze ... ale jeśli masz część kodu, który jest wolny i używa EF, przetestuj z bezpośrednim TSQL i porównaj wydajność zamiast polegać na założeniach ( .Any()
to ZAWSZE jest szybsze niż .Count() > 0
).
Chociaż zgadzam się z większością głosowanych odpowiedzi i komentarzy - szczególnie w kwestii Any
, w której deweloper sygnalizuje zamiary lepsze niż Count() > 0
- miałem sytuację, w której Count jest szybszy o rząd wielkości na SQL Server (EntityFramework 4).
Oto zapytanie z Any
tym wyjątkiem limitu czasu (w ~ 200 000 rekordów):
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
wersja wykonywana w ciągu milisekund:
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();
Muszę znaleźć sposób, aby zobaczyć, jaki dokładnie SQL produkują oba LINQ - ale jest oczywiste, że istnieje ogromna różnica w wydajności między, Count
a Any
w niektórych przypadkach, i niestety wydaje się, że nie możesz po prostu trzymać się Any
we wszystkich przypadkach.
EDYCJA: Tutaj są generowane zapytania SQL. Piękności jak widać;)
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],
[Projekt2]. [Utworzono] AS [Utworzono]
OD (WYBIERZ [Projekt2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Utworzono] AS [Utworzono], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
OD (WYBIERZ
[Extent1]. [ContactId] AS [ContactId],
[Extent1]. [CompanyId] AS [CompanyId],
[Extent1]. [ContactName] AS [ContactName],
[Extent1]. [FullName] AS [FullName],
[Extent1]. [ContactStatusId] AS [ContactStatusId],
[Extent1]. [Utworzono] AS [Utworzono]
FROM [dbo]. [Kontakt] AS [Extent1]
GDZIE ([Extent1]. [CompanyId] = @ p__linq__0) ORAZ ([Extent1]. [ContactStatusId] <= 3) ORAZ (NIE ISTNIEJE (WYBIERZ
1 AS [C1]
FROM [dbo]. [NewsletterLog] AS [Extent2]
GDZIE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) ORAZ (6 = [Extent2]. [NewsletterLogTypeId])
))
) AS [Project2]
) AS [Project2]
GDZIE [Projekt2]. [Numer_wiersza]> 99
ZAMÓW PRZEZ [Projekt2]. [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],
[Projekt2]. [Utworzono] AS [Utworzono]
OD (WYBIERZ [Projekt2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Utworzono] AS [Utworzono], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
OD (WYBIERZ
[Projekt1]. [ContactId] AS [ContactId],
[Projekt1]. [CompanyId] AS [CompanyId],
[Project1]. [ContactName] AS [ContactName],
[Projekt1]. [Pełna nazwa] AS [Pełna nazwa],
[Projekt1]. [ContactStatusId] AS [ContactStatusId],
[Projekt1]. [Utworzono] AS [Utworzono]
OD (WYBIERZ
[Extent1]. [ContactId] AS [ContactId],
[Extent1]. [CompanyId] AS [CompanyId],
[Extent1]. [ContactName] AS [ContactName],
[Extent1]. [FullName] AS [FullName],
[Extent1]. [ContactStatusId] AS [ContactStatusId],
[Extent1]. [Utworzono] AS [Utworzono],
(WYBIERZ
COUNT (1) AS [A1]
FROM [dbo]. [NewsletterLog] AS [Extent2]
GDZIE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) ORAZ (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
FROM [dbo]. [Kontakt] AS [Extent1]
) AS [Project1]
GDZIE ([Projekt1]. [CompanyId] = @ p__linq__0) ORAZ ([Projekt1]. [ContactStatusId] <= 3) ORAZ (0 = [Projekt1]. [C1])
) AS [Project2]
) AS [Project2]
GDZIE [Projekt2]. [Numer_wiersza]> 99
ZAMÓW PRZEZ [Projekt2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
Wydaje się, że czysty Where with EXISTS działa znacznie gorzej niż obliczanie Count, a następnie wykonywanie Where with Count == 0.
Daj mi znać, jeśli zauważysz jakiś błąd w moich ustaleniach. Bez względu na dyskusję Any vs. Count można wyciągnąć z tego wszystko, że bardziej skomplikowane LINQ jest o wiele lepiej, gdy zostanie przepisane jako procedura przechowywana;).