Why is this LINQ so much slower than its SQL counterpart?

旧城冷巷雨未停 提交于 2019-12-13 02:59:12

问题


I have the below LINQ to SQL method that takes an inordinate amount of time to execute yet its SQL counterpart is quite simple and fast. Am I doing something wrong in the LINQ part? I am just trying to return some data to display, read-only, in a Data Grid.

I understand that if the tool doesn't fit don't use it and as such I could just do a SQL call here but I'd like to understand why there is such a difference.

Below is the LINQ and then the SQL that it dumps.

    public static DataTable GetEnrolledMembers(Guid workerID)
    {
        using (var DB = CmoDataContext.Create())
        {                
            var AllEnrollees = from enrollment in DB.tblCMOEnrollments
                               where enrollment.CMOSocialWorkerID == workerID || enrollment.CMONurseID == workerID
                               join supportWorker in DB.tblSupportWorkers on enrollment.EconomicSupportWorkerID
                                   equals supportWorker.SupportWorkerID into workerGroup
                               from worker in workerGroup.DefaultIfEmpty()
                               select
                                   new
                                       {
                                           enrollment.ClientID,
                                           enrollment.CMONurseID,
                                           enrollment.CMOSocialWorkerID,
                                           enrollment.EnrollmentDate,
                                           enrollment.DisenrollmentDate,
                                           ESFirstName = worker.FirstName,
                                           ESLastName = worker.LastName,
                                           ESPhone = worker.Phone
                                       };

            var result = from enrollee in AllEnrollees.AsEnumerable()
                         where (enrollee.DisenrollmentDate == null || enrollee.DisenrollmentDate > DateTime.Now)
                         let lastName = BLLConnect.MemberLastName(enrollee.ClientID)
                         let firstName = BLLConnect.MemberFirstName(enrollee.ClientID)
                         orderby enrollee.DisenrollmentDate ascending , lastName ascending
                         select new
                             {
                                 enrollee.ClientID,
                                 LastName = lastName,
                                 FirstName = firstName,
                                 NurseName = BLLAspnetdb.NurseName(enrollee.CMONurseID),
                                 SocialWorkerName = BLLAspnetdb.SocialWorkerName(enrollee.CMOSocialWorkerID),
                                 enrollee.EnrollmentDate,
                                 enrollee.DisenrollmentDate,
                                 ESWorkerName = enrollee.ESFirstName + " " + enrollee.ESLastName,
                                 enrollee.ESPhone
                             };

            DB.Log = Console.Out;
            return result.CopyLinqToDataTable();
        }
    }

And the SQL:

SELECT [t0].[ClientID], [t0].[CMONurseID], [t0].[CMOSocialWorkerID], [t0].[EnrollmentDate], [t0].[DisenrollmentDate], [t1].[FirstName] AS [ESFirstName], [t1].[LastName] AS [ESLastName], [t1].[Phone] AS [ESPhone]
FROM [dbo].[tblCMOEnrollment] AS [t0]
LEFT OUTER JOIN [dbo].[tblSupportWorker] AS [t1] ON [t0].[EconomicSupportWorkerID] = ([t1].[SupportWorkerID])
WHERE ([t0].[CMOSocialWorkerID] = @p0) OR ([t0].[CMONurseID] = @p1)
-- @p0: Input UniqueIdentifier (Size = 0; Prec = 0; Scale = 0) [060632ee-be09-4057-b17b-2d0190d0ff74]
-- @p1: Input UniqueIdentifier (Size = 0; Prec = 0; Scale = 0) [060632ee-be09-4057-b17b-2d0190d0ff74]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.4926

回答1:


First, I don't think you are comparing apples to apples, you have plenty of BllConnect.Something calls around the select of the second linq query. Additionally you have to take out the AsEnumerable as mentioned in the other answer.

Consider the below (given you add the corresponding relations in the db and/or linq2sql designer):

public static DataTable GetEnrolledMembers(Guid workerID)
{
    using (var DB = CmoDataContext.Create())
    {                
        var AllEnrollees = from enrollment in DB.tblCMOEnrollments
                           where enrollment.CMOSocialWorkerID == workerID 
                                 || enrollment.CMONurseID == workerID
                           let w = enrollment.EconomicSupporterWorker
                           select new
                                   {
                                       enrollment.ClientID,
                                       enrollment.CMONurseID,
                                       enrollment.CMOSocialWorkerID,
                                       enrollment.EnrollmentDate,
                                       enrollment.DisenrollmentDate,
                                       ESFirstName = w != null ? w.FirstName : null,
                                       ESLastName = w != null ? w.LastName : null,
                                       ESPhone = w != null ? w.Phone : null
                                   };
        var filteredEnrollees = AllEnrollees
            .Where(e=> e.DisenrollmentDate == null || e.DisenrollmentDate > DateTime.Now);
        //benchmark how much it delays if you do a .ToList until here
        // ... when comparing the sql, run it on the same remote computer you are running this, 
        // so you take into account the time to transfer the data.
        filteredEnrollees = filteredEnrollees 
            .OrderBy(e=> e.DisenrollmentData) // benchmark here again
            .ThenBy(e=> BLLConnect.MemberLastName(enrollee.ClientID)); // prob. causing issues
        var result = // do what you already had, but against filteredEnrollees and benchmark
        // prob. issues with BllConnect.* and BllAspnetdb.* being called for each record / 
        // ... doesn't happen in sql side

        DB.Log = Console.Out;
        return result.CopyLinqToDataTable();
    }
}



回答2:


By adding AsEnumerable():

var result = from enrollee in AllEnrollees.AsEnumerable()
            ...

You're forcing the first query to be evaluated completely, and every record (AllEnrollees) to be fetched from the DB.

With your SQL statement, you're doing all of the filtering on the server, which will be much faster.



来源:https://stackoverflow.com/questions/3875154/why-is-this-linq-so-much-slower-than-its-sql-counterpart

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!