[.net] ما هي الطريقة الأفضل للأداء: .Any () مقابل .Count ()> 0؟


Answers

ملاحظة: كتبت هذا الجواب عندما كان Entity Framework 4 فعليًا. لم يكن الهدف من هذه الإجابة هو الوصول إلى اختبار الأداء .Count() مقابل .Count() . كانت النقطة هي الإشارة إلى أن EF أبعد ما يكون عن الكمال. تكون الإصدارات الأحدث أفضل ... ولكن إذا كان لديك جزء من التعليمة البرمجية بطيء ويستخدم EF ، فاختبر TSQL مباشرة وقارن الأداء بدلاً من الاعتماد على افتراضات (ذلك .Count() > 0 دائمًا أسرع من .Count() > 0 ) .

على الرغم من أنني أتفق مع معظم الإجابات والتعليقات التي تم التصويت عليها - وخاصةً على النقطة التي ترغب فيها Any مطور إشارات بشكل أفضل من Count() > 0 - لقد كان الوضع فيه أسرع في الترتيب حسب الحجم على SQL Server (EntityFramework 4).

فيما يلي الاستعلام مع Any استثناء anw مهلة (على السجلات 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 شيء في كل الحالات.

تحرير: هنا يتم إنشاء SQLs. الجمال كما ترون؛)

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

يبدو أن نقية أين مع EXISTS يعمل أسوأ بكثير من حساب عدد ثم القيام به مع Count == 0.

اسمحوا لي أن أعرف ما إذا كان الرجال يرون بعض الخطأ في النتائج التي توصلت إليها. ما يمكن أخذه من كل هذا بغض النظر عن مناقشة أي عد ضد هو أن أي LINQ أكثر تعقيدا هو أفضل حالا عند إعادة كتابتها كإجراء مخزّن ؛).

Question

في مساحة الاسم System.Linq ، يمكننا الآن أن System.Linq IEnumerable بنا للحصول على أساليب ملحق Any() و Count() .

لقد تم إخباري مؤخراً أنه إذا أردت التحقق من أن مجموعة تحتوي على عنصر واحد أو أكثر داخله ، فيجب استخدام طريقة ملحق .Count() > 0 بدلاً من طريقة ملحق .Count() > 0 لأن طريقة ملحق .Count() يجب أن تتكرر من خلال جميع العناصر.

ثانيًا ، تحتوي بعض المجموعات على خاصية (ليست طريقة تمديد) هي Count أو Length . هل من الأفضل استخدام تلك بدلاً من .Count() أو .Count() ؟

نعم / ناي؟




تحرير: تم إصلاحه في EF الإصدار 6.1.1. وهذه الإجابة ليست أكثر واقعية

بالنسبة لـ SQL Server و EF4-6 ، يقوم Count () أسرع مرتين تقريبًا من أي ().

عند تشغيل 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]

يتطلب فحصين من الصفوف مع حالتك.

لا أحب أن أكتب 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;
    }
}



يعتمد ذلك ، ما مدى حجم مجموعة البيانات وما هي متطلبات أدائك؟

إن لم يكن استخدامًا هائلاً ، فإن الشكل الأكثر قابلية للقراءة ، والذي هو بنفسي ، لأنه أقصر وقابل للقراءة وليس معادلة.




حول طريقة Count () ، إذا كان IEnumarable عبارة عن ICollection ، فعندئذ لا يمكننا التكرار عبر جميع العناصر لأننا نستطيع استرداد حقل Count من ICollection ، إذا لم يكن IEnumerable عبارة عن ICOLlection ، يجب علينا التكرار عبر جميع العناصر باستخدام فترة مع على 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;
}

المرجع: المصدر المرجعي المعدود