[.net] .Any () vs .Count ()> 0 중 어떤 방법이 더 잘 수행됩니까?

System.Linq네임 스페이스, 우리는 지금 우리의 확장 할 수 는 IEnumerable이야 가지고 모든 ()를 하고 () 카운트 확장 방법 .

컬렉션에 하나 이상의 항목이 포함되어 있는지 확인 하려면 확장 방법이 모든 항목을 반복 해야하기 때문에 .Any()확장 방법 대신 확장 방법을 사용해야한다고 최근에 들었습니다 ..Count() > 0.Count()

둘째, 일부 컬렉션에는 또는 확장 속성 이 아닌 속성 이 있습니다. 또는 대신에 그것들을 사용하는 것이 더 좋을까요?CountLength.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>.CountICollection

물론 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의 값을 확인하십시오.