Programing

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

crosscheck 2020. 10. 4. 10:29
반응형

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


System.Linq네임 스페이스, 우리는 지금 우리의 확장 할 수 있습니다 IEnumerable가지고 '들 Any()Count() 확장 방법을 .

최근에 컬렉션에 하나 이상의 항목이 포함되어 있는지 확인 하려면 확장 메서드가 모든 항목을 반복 해야하므로 .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-에이-개체 참고 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신호 의도 개발자 보다 나은 Count() > 0- 나는 백작이 SQL 서버 (EntityFramework 4)에 크기의 명령에 의해 빠르게하는 상황을 했어.

다음은 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) 
[Project2]. [ContactId] AS [ContactId], 
[Project2]. [CompanyId] AS [CompanyId], 
[Project2]. [ContactName] AS [ContactName], 
[Project2]. [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 (SELECT 
        [Extent1]. [ContactId] AS [ContactId], 
        [Extent1]. [CompanyId] AS [CompanyId], 
        [Extent1]. [ContactName] AS [ContactName], 
        [Extent1]. [FullName] AS [FullName], 
        [Extent1]. [ContactStatusId] AS [ContactStatusId], 
        [Extent1]. [작성 됨] AS [작성 됨]
        FROM [dbo]. [연락처] AS [Extent1]
        WHERE ([Extent1]. [CompanyId] = @ p__linq__0) AND ([Extent1]. [ContactStatusId] <= 3) AND (NOT EXISTS (SELECT 
            1 AS [C1]
            FROM [dbo]. [NewsletterLog] AS [Extent2]
            WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])
        ))
    ) 그대로 [프로젝트 2]
) 그대로 [프로젝트 2]
WHERE [프로젝트 2]. [행 _ 번호]> 99
ORDER BY [Project2]. [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], 
[프로젝트 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 (SELECT 
        [Project1]. [ContactId] AS [ContactId], 
        [Project1]. [CompanyId] AS [CompanyId], 
        [Project1]. [ContactName] AS [ContactName], 
        [Project1]. [FullName] AS [FullName], 
        [Project1]. [ContactStatusId] AS [ContactStatusId], 
        [프로젝트 1]. [생성됨] AS [생성됨]
        FROM (SELECT 
            [Extent1]. [ContactId] AS [ContactId], 
            [Extent1]. [CompanyId] AS [CompanyId], 
            [Extent1]. [ContactName] AS [ContactName], 
            [Extent1]. [FullName] AS [FullName], 
            [Extent1]. [ContactStatusId] AS [ContactStatusId], 
            [Extent1]. [Created] AS [Created], 
            (고르다 
                COUNT (1) AS [A1]
                FROM [dbo]. [NewsletterLog] AS [Extent2]
                WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
            FROM [dbo]. [연락처] AS [Extent1]
        ) 그대로 [프로젝트 1]
        WHERE ([Project1]. [CompanyId] = @ p__linq__0) AND ([Project1]. [ContactStatusId] <= 3) AND (0 = [Project1]. [C1])
    ) 그대로 [프로젝트 2]
) 그대로 [프로젝트 2]
WHERE [프로젝트 2]. [행 _ 번호]> 99
ORDER BY [Project2]. [ContactId] ASC ', N'@ p__linq__0 int ', @ p__linq__0 = 4

순수한 Where with EXISTS는 Count를 계산 한 다음 Count == 0으로 Where를 수행하는 것보다 훨씬 나쁘게 작동합니다.

Let me know if you guys see some error in my findings. What can be taken out of all this regardless of Any vs Count discussion is that any more complex LINQ is way better off when rewritten as Stored Procedure ;).


Since this is rather popular topic and answers differ I had to take a fresh look on problem.

Testing env: EF 6.1.3, SQL Server, 300k records

Table model:

class TestTable
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public string Surname { get; set; }
}

Test code:

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();
        }
    }
}

Results:

Any() ~ 3ms

Count() ~ 230ms for first query, ~ 400ms for second

Remarks:

For my case EF didn't generate SQL like @Ben mentioned in his post.


EDIT: it was fixed in EF version 6.1.1. and this answer is no more actual

For SQL Server and EF4-6, Count() performs about two times faster than Any().

When you run Table.Any(), it will generate something like(alert: don't hurt the brain trying to understand it)

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]

that requires 2 scans of rows with your condition.

I don't like to write Count() > 0 because it hides my intention. I prefer to use custom predicate for this:

public static class QueryExtensions
{
    public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
    {
        return source.Count(predicate) > 0;
    }
}

It depends, how big is the data set and what are your performance requirements?

If it's nothing gigantic use the most readable form, which for myself is any, because it's shorter and readable rather than an equation.


About the Count() method, if the IEnumarable is an ICollection, then we can't iterate across all items because we can retrieve the Count field of ICollection, if the IEnumerable is not an ICollection we must iterate across all items using a while with a MoveNext, take a look the .NET Framework Code:

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;
}

Reference: Reference Source Enumerable


You can make a simple test to figure this out:

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;

Check the values of testCount and testAny.


If you are using the Entity Framework and have a huge table with many records Any() will be much faster. I remember one time I wanted to check to see if a table was empty and it had millions of rows. It took 20-30 seconds for Count() > 0 to complete. It was instant with Any().

Any() can be a performance enhancement because it may not have to iterate the collection to get the number of things. It just has to hit one of them. Or, for, say, LINQ-to-Entities, the generated SQL will be IF EXISTS(...) rather than SELECT COUNT ... or even SELECT * ....

참고URL : https://stackoverflow.com/questions/305092/which-method-performs-better-any-vs-count-0

반응형