에 System.Linq
네임 스페이스, 우리는 지금 우리의 확장 할 수 는 IEnumerable이야 가지고 모든 ()를 하고 () 카운트 확장 방법 .
컬렉션에 하나 이상의 항목이 포함되어 있는지 확인 하려면 확장 방법이 모든 항목을 반복 해야하기 때문에 .Any()
확장 방법 대신 확장 방법을 사용해야한다고 최근에 들었습니다 ..Count() > 0
.Count()
둘째, 일부 컬렉션에는 또는 확장 속성 이 아닌 속성 이 있습니다. 또는 대신에 그것들을 사용하는 것이 더 좋을까요?Count
Length
.Any()
.Count()
응 / 내?
답변
당신이를 가지고 뭔가 시작하는 경우 .Length
또는 .Count
(예 ICollection<T>
, IList<T>
, List<T>
, 등) – 그것이를 통해 갈 필요가 없기 때문에 다음이 가장 빠른 옵션이 될 것입니다 GetEnumerator()
/ MoveNext()
/ Dispose()
에 필요한 순서 Any()
비어를 확인하는 IEnumerable<T>
순서 .
단지를 들어 IEnumerable<T>
, 다음 Any()
것이다 일반적으로 그것은 단지 하나의 반복을보고하는 등, 빠르게합니다. 그러나 LINQ-to-Objects 구현은 ( 최적화로 사용하여) Count()
확인 하므로 기본 데이터 소스가 직접 목록 / 컬렉션이라면 큰 차이가 없습니다. 그것이 제네릭이 아닌 이유를 사용하지 않는 이유를 묻지 마십시오 …ICollection<T>
.Count
ICollection
물론 LINQ를 사용하여 필터 등을 필터링 한 경우 Where
반복자 블록 기반 시퀀스 ICollection<T>
가 있으므로이 최적화는 쓸모가 없습니다.
일반적으로 IEnumerable<T>
: Any()
;-p 와 스틱
답변
참고 : Entity Framework 4가 실제 일 때이 답변을 썼습니다. 이 답변의 요점은 사소한에 들어갈 아니었다 .Any()
대 .Count()
성능 테스트. 요점은 EF가 완벽하지 않다는 신호였다. 최신 버전이 더 낫지 만 … 코드의 속도가 느리고 EF를 사용하는 경우 가정에 의존하지 않고 직접 TSQL로 테스트하고 성능을 비교하십시오 ( .Any()
항상보다 빠름 .Count() > 0
).
특히 개발자가 의도 한 것보다 더 나은 Any
신호 에 대해 가장 많이 찬성 된 답변과 의견에 동의하지만 SQL Server에서 Count가 더 빠르다는 상황이있었습니다 (EntityFramework 4).Count() > 0
다음은 Any
시간 초과 예외 가있는 쿼리입니다 (~ 200.000 레코드).
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 모두 LINQs 생산 무엇을 볼 수있는 방법을 찾아야 -하지만 사이에 큰 성능 차이가 명백 Count
하고 Any
경우에 따라서는, 불행하게도 당신이 단지 고집 할 수없는 것 같습니다 Any
모든 경우입니다.
편집 : 여기에 생성 된 SQL이 있습니다. 당신이 볼 수 있듯이 아름다움;)
ANY
:
exec sp_executesql N'SELECT TOP (1) [프로젝트 2]. [ContactId] AS [ContactId], [프로젝트 2]. [회사 ID] AS [회사 ID], [프로젝트 2]. [ContactName] AS [ContactName], [프로젝트 2]. [FullName] AS [FullName], [Project2]. [ContactStatusId] AS [ContactStatusId], [프로젝트 2]. [만든] AS [만든] 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 (선택 [Extent1]. [ContactId] AS [ContactId], [Extent1]. [회사 ID] AS [회사 ID], [Extent1]. [ContactName] AS [ContactName], [Extent1]. [FullName] AS [FullName], [Extent1]. [ContactStatusId] AS [ContactStatusId], [Extent1]. [만든] AS [만든] [dbo]에서. [문의] AS [Extent1] ([Extent1]. [CompanyId] = @ p__linq__0) AND ([Extent1]. [ContactStatusId] <= 3) AND (존재하지 않음 (SELECT 1 AS [C1] [dbo]에서. [뉴스 레터 로그] AS [Extent2] ([Extent1]. [ContactId] = [Extent2]. [ContactId]) 및 (6 = [Extent2]. [NewsletterLogTypeId]) )) ) AS [프로젝트 2] ) AS [프로젝트 2] 어디에 [프로젝트 2]. [행 번호]> 99 [프로젝트 2]에 의한 주문. [ContactId] ASC ', N'@ p__linq__0 int ', @ p__linq__0 = 4
COUNT
:
exec sp_executesql N'SELECT TOP (1) [프로젝트 2]. [ContactId] AS [ContactId], [프로젝트 2]. [회사 ID] AS [회사 ID], [프로젝트 2]. [ContactName] AS [ContactName], [프로젝트 2]. [FullName] AS [FullName], [Project2]. [ContactStatusId] AS [ContactStatusId], [프로젝트 2]. [만든] AS [만든] 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 (선택 [프로젝트 1]. [ContactId] AS [ContactId], [프로젝트 1]. [회사 ID] AS [회사 ID], [프로젝트 1]. [ContactName] AS [ContactName], [프로젝트 1]. [FullName] AS [FullName], [Project1]. [ContactStatusId] AS [ContactStatusId], [프로젝트 1]. [만든] AS [만든] FROM (선택 [Extent1]. [ContactId] AS [ContactId], [Extent1]. [회사 ID] AS [회사 ID], [Extent1]. [ContactName] AS [ContactName], [Extent1]. [FullName] AS [FullName], [Extent1]. [ContactStatusId] AS [ContactStatusId], [Extent1]. [만든] AS [만든], (고르다 COUNT (1)대로 [A1] [dbo]에서. [뉴스 레터 로그] AS [Extent2] WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) 및 (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1] [dbo]에서. [문의] AS [Extent1] ) AS [프로젝트 1] ([Project1]. [CompanyId] = @ p__linq__0) AND ([Project1]. [ContactStatusId] <= 3) AND (0 = [프로젝트 1]. [C1]) ) AS [프로젝트 2] ) AS [프로젝트 2] 어디에 [프로젝트 2]. [행 번호]> 99 [프로젝트 2]에 의한 주문. [ContactId] ASC ', N'@ p__linq__0 int ', @ p__linq__0 = 4
EXISTS를 사용한 순수한 위치는 Count를 계산 한 다음 Count == 0으로 Where를 수행하는 것보다 훨씬 나쁩니다.
내 결과에 약간의 오류가 있는지 알려주십시오. Any vs Count 토론에 관계없이이 모든 것을 제거 할 수있는 것은 더 복잡한 LINQ가 Stored Procedure;
답변
이것은 다소 인기있는 주제이며 답변이 다르므로 문제를 새롭게 살펴 봐야했습니다.
테스트 환경 :
EF 6.1.3, SQL Server, 300k 레코드
테이블 모델 :
class TestTable
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
테스트 코드 :
class Program
{
static void Main()
{
using (var context = new TestContext())
{
context.Database.Log = Console.WriteLine;
context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);
Console.ReadLine();
}
}
}
결과 :
Any () ~ 3ms
Count () ~ 첫 번째 쿼리의 경우 230ms, 두 번째 ~ 400ms
비고 :
필자의 경우 EF는 그의 게시물에서 언급 한 @ Ben과 같은 SQL을 생성하지 않았습니다.
답변
편집 : EF 버전 6.1.1에서 수정되었습니다. 이 답변은 더 이상 실제적이지 않습니다
SQL Server 및 EF4-6의 경우 Count ()는 Any ()보다 약 2 배 더 빠릅니다.
당신이 Table.Any ()를 실행하면 같은 생성 ( 경고 : 그것을 이해하려고 노력 두뇌를 다치게하지 않아 )
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [Table] AS [Extent1]
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM [Table] AS [Extent2]
)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
조건에 따라 2 행의 스캔이 필요합니다.
Count() > 0
내 의도를 숨기고 쓰기를 좋아하지 않습니다 . 나는 이것을 위해 custom 술어를 사용하는 것을 선호합니다 :
public static class QueryExtensions
{
public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
return source.Count(predicate) > 0;
}
}
답변
데이터 세트의 크기와 성능 요구 사항은 무엇입니까?
그것이 엄청나게 큰 것이 아니라면 가장 읽기 쉬운 형태를 사용하십시오.
답변
[정보 수 () 경우 생성 방법, IEnumarable은 입니다 ICollection에가 , 우리가 할 수없는 반복 처리는 모든 항목에 걸쳐 우리는 검색 할 수 있기 때문에 카운트 의 필드 은 ICollection을 경우 는 IEnumerable이 아니 어서 ICollection은 우리가 반복 처리가 모든 항목에 걸쳐를 사용한다 반면 에이 MoveNext는은 , 닷넷 프레임 워크 코드를 살펴 :
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null)
return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null)
return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
checked
{
while (e.MoveNext()) count++;
}
}
return count;
}
참조 : 참조 소스 열거 가능
답변
이것을 알아 내기 위해 간단한 테스트를 할 수 있습니다 :
var query = //make any query here
var timeCount = new Stopwatch();
timeCount.Start();
if (query.Count > 0)
{
}
timeCount.Stop();
var testCount = timeCount.Elapsed;
var timeAny = new Stopwatch();
timeAny.Start();
if (query.Any())
{
}
timeAny.Stop();
var testAny = timeAny.Elapsed;
testCount 및 testAny의 값을 확인하십시오.