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


577

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

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

둘째, 일부 컬렉션에는 또는 확장 속성 이 아닌 속성 이 있습니다. 또는 대신에 그것들을 사용하는 것이 더 좋을까요?CountLength.Any().Count()

응 / 내?


열거 형에 Any ()를 사용하고 컬렉션에 포함하는 것이 좋습니다. 누군가가 '(somecollection.Count> 0)'이라고 쓰면 혼란 스럽거나 가독성 문제가 발생할 경우 확장 메소드 이름으로 Any ()로 작성하는 것이 좋습니다. 그런 다음 모두 만족했습니다. 가독성뿐만 아니라 성능 측면에서도 우수합니다. 따라서 모든 코드의 일관성을 유지하고 프로젝트의 개별 개발자가 Count vs Any를 선택할 필요가 없습니다.
Mahesh Bongani

답변:


708

당신이를 가지고 뭔가 시작하는 경우 .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 와 스틱


9
Marc : ICollection <T>는 실제로 ICollection에서 파생되지 않습니다. 나도 놀랐지 만 리플렉터는 거짓말을하지 않았다.
Bryan Watts

7
Any () 구현이 ICollection 인터페이스를 확인하지 않고 Count 속성을 확인하지 않습니까?
derigel

313
대부분 Any ()를 사용하는 또 다른 이유가 있다고 생각합니다. 그것은 개발자의 정확한 의도를 알립니다. 항목의 수를 알고 싶지는 않지만 일부가있는 경우에만 somecollection.Any ()가 somecollection보다 간단하고 명확합니다. Count> 0
TJKjaer

13
@huttelihut-성명서에 실제로 혼란을 느끼는 개발자는 몇 명 (somecollection.Count > 0)입니까? LINQ의 .Any () 메소드를 도입하기 전에 모든 코드를 이해하기 어려웠습니까?
CraigTP

25
@JLRishe-여전히 someCollection.Count > 0똑같이 명확하고 someCollection.Any()성능 이 뛰어나고 LINQ가 필요하지 않다는 이점이 있습니다. 물론 이것은 매우 간단한 경우이며 LINQ 연산자를 사용하는 다른 구문은 개발자에게 동등한 비 LINQ 옵션보다 훨씬 더 명확한 의도를 전달합니다.
CraigTP

65

참고 : 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;


2
각 시나리오에 대해 각 linq-query에 의해 생성되는 일부 SQL 쿼리 계획을보고 싶습니다.
Pure.Krome 2016 년

43
SQL을 기반으로 할 수있는 것은 두 쿼리가 끔찍한 것입니다. 나는 ... 나는 보통 내 자신의 TSQL을 쓸 이유가 알고 있었다
마크 Gravell

! Count와 마찬가지로 모든 행을 살펴 봐야합니다. 귀하의 예가 그런 끔찍한 결과를 제공한다는 것은 최악의 경우입니다. 최악의 경우! 모두 Count보다 약간 느려 야합니다. 귀하의 경우 선택을 단순화하는 방법을 찾고 싶습니다. 아마 선택 단계를 나누거나 가능한 경우 조건을 재정렬합니다. 그러나 Any가 Count보다 낫다는 규칙은지지 않습니다! Any가 Count보다 낫다는 것은 매우 좋은 것입니다.
벤트

25

이것은 다소 인기있는 주제이며 답변이 다르므로 문제를 새롭게 살펴 봐야했습니다.

테스트 환경 : 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을 생성하지 않았습니다.


4
적절한 비교를 위해서는해야 Count() > 0합니다. : D
Andrew

1
Andrew, Count ()> 0은이 특정 테스트에서 Count ()와 다르게 실행되지 않습니다.
CodeMonkeyForHire

11

편집 : 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;
    }
}

나는 이것도 보았다. Any () SQL은 전혀 의미가 없습니다. 왜 그들이하지 않는지 잘 모르겠습니다 : CASE WHEN (EXISTS (sql)) THEN 1 ELSE 0 END. 나는 그들이 0을 반환하기 위해 존재하지 않는 이유를 생각할 수 없다.
scott.korin

이것은 거짓입니다. 무작위로 잘못된 쿼리 계획을 찾았습니다. 이런 일이 발생합니다. 거의 항상 빠릅니다.
usr

6.1.3에서 생성 된 sql을 확인한 결과 다음과 같이 수정되었습니다. THEN 캐스트 (비트 1) ELSE 캐스트 (비트 0) END AS [C1] FROM (SELECT 1 AS X) AS [SingleRowTable1]
Ben

6

데이터 세트의 크기와 성능 요구 사항은 무엇입니까?

그것이 엄청나게 큰 것이 아니라면 가장 읽기 쉬운 형태를 사용하십시오.


2

[정보 수 () 경우 생성 방법, 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;
}

참조 : 참조 소스 열거 가능


2

이것을 알아 내기 위해 간단한 테스트를 할 수 있습니다 :

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


1
여기에 모든 대 Count 속성에 대한 코드 테스트 () 대 속성 승리 카운트입니다 + 2 배와 모든 () - 링크
Stanislav Prusac

1
더 나은 결과를 얻으려면 이러한 비교를 1000 회 이상 수행 할 수 있습니다. 결과를 평균화하고 임의의 스파이크를 피하는 데 도움이됩니다.
로마

위에서 언급 한 방법과 같이 테스트 할 때는 데이터베이스 / 네트워크로드, 데이터베이스 측 캐싱 계획 등과 같은 더 많은 요소를 고려해야합니다. 따라서 정확한 테스트를 수행하려면 격리되고 정확한 환경도 설계해야합니다.
Vahid Farahmandian

더 나은 비교를 위해서는 Count속성이 아닌 Count () 대 .Any () 메소드 로 대체 해야합니다 . 반복 시간이 필요합니다.
daremachine

0

Entity Framework를 사용하고 있고 레코드가 많은 거대한 테이블 이 있으면 Any () 가 훨씬 빠릅니다. 테이블이 비어 있고 수백만 행이 있는지 확인하고 싶었던 적이 있습니다. Count ()> 0을 완료하는 데 20-30 초가 걸렸습니다. Any () 와 즉각적이었다 .

Any () 는 많은 수를 얻기 위해 콜렉션을 반복 할 필요가 없기 때문에 성능 향상이 될 수 있습니다. 그것은 그들 중 하나를 공격해야합니다. 또는 LINQ-to-Entities의 경우 생성 된 SQL은 SELECT COUNT ... 또는 심지어 SELECT * ...가 아니라 IF EXISTS (...)가됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.