[.net] 더 나은 성능을내는 메소드 : .Any () vCount ()> 0?



Answers

참고 : Entity Framework 4가 실제 일 때이 답변을 작성했습니다. 이 대답의 요점은 사소한 일이 아니었다. 모든 .Any() 대. .Count() 성능 테스트. 요점은 EF가 완벽하지 못하다는 신호입니다. 최신 버전은 더 좋지만 코드가 느리고 EF를 사용하는 경우 직접 TSQL로 테스트하고 가정에 의존하지 않고 성능을 비교하면 ( .Any().Count() > 0 보다 항상 빠름) .

가장 중요한 답변 및 의견에 동의하는 동안 - 특히 신호 개발자Count() > 0 보다 더 나은 의도 를 보이는 점에 대해 - 저는 Count가 SQL Server (EntityFramework 4)에서 더 빠르다고 생각했습니다.

다음은이 시간 초과 예외 (~ 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();

정확한 LINQ가 생성되는 SQL을 찾는 방법을 찾아야합니다. 그러나 CountAny 사이의 성능 차이가 큰 경우가 있습니다. 불행히도 모든 경우에 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], 
[Project2].[Created] AS [Created]
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].[Created] AS [Created]
        FROM [dbo].[Contact] 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])
        ))
    )  AS [Project2]
)  AS [Project2]
WHERE [Project2].[row_number] > 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], 
[Project2].[Created] AS [Created]
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], 
        [Project1].[Created] AS [Created]
        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], 
            (SELECT 
                COUNT(1) AS [A1]
                FROM [dbo].[NewsletterLog] AS [Extent2]
                WHERE ([Extent1].[ContactId] = [Extent2].[ContactId]) AND (6 = [Extent2].[NewsletterLogTypeId])) AS [C1]
            FROM [dbo].[Contact] AS [Extent1]
        )  AS [Project1]
        WHERE ([Project1].[CompanyId] = @p__linq__0) AND ([Project1].[ContactStatusId] <= 3) AND (0 = [Project1].[C1])
    )  AS [Project2]
)  AS [Project2]
WHERE [Project2].[row_number] > 99
ORDER BY [Project2].[ContactId] ASC',N'@p__linq__0 int',@p__linq__0=4

순수한 Where With EXISTS는 Count 계산보다 더 나쁜 작업을하고 Count with == 0을 사용합니다.

네가 내 연구 결과에 약간의 오류가 있다면 알려주지. Any versus Count 논의와 상관없이이 모든 것에서 벗어날 수있는 것은 더 복잡한 LINQ는 저장 프로 시저로 재 작성 될 때 더 효율적이라는 것입니다.

Question

System.Linq 네임 스페이스에서 이제 IEnumerableAny()Count() 확장 메서드를 추가 할 수 있습니다 .

나는 최근 컬렉션에 하나 이상의 아이템이 포함되어 있는지 확인하고 싶다면 .Any() 확장 메소드 대신 .Count() > 0 확장 메소드 대신 .Any() 확장 메소드를 사용해야한다. 모든 항목을 반복해야합니다.

둘째, 일부 콜렉션에는 Count 또는 Length 특성 (확장 메소드가 아님)이 있습니다. .Any() 또는 .Count() 대신에 이들을 사용하는 것이 더 좋을까요?

참으로 / 응?




IEnumarableICollection 인 경우 Count () 메서드에 대해 ICollectionCount 필드를 검색 할 수 있으므로 모든 항목을 반복 할 수 없습니다. IEnumerableICollection 이 아닌 경우 잠시 동안 모든 항목에 대해 반복해야합니다. MoveNext , .NET Framework 코드 살펴보기 :

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

참조 : 참조 소스 Enumerable




데이터 세트가 얼마나 큰지, 실적 요구 사항은 무엇입니까?

거대한 것은 아무것도 아니지만, 가장 독해 가능한 형식을 사용하십시오. 이것은 나 자신을위한 것으로, 방정식 이라기보다 짧고 읽기 쉽기 때문입니다.




편집 : 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 을 쓰는 것을 좋아하지 않습니다. 나는 이것을 위해 사용자 정의 술어를 선호한다.

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



Links