【问题标题】:Why is this LINQ so much slower than its SQL counterpart?为什么这个 LINQ 比它的 SQL 慢很多?
【发布时间】:2010-10-06 17:15:09
【问题描述】:

我有下面的 LINQ to SQL 方法,它需要大量的时间来执行,但它的 SQL 对应物非常简单和快速。我在 LINQ 部分做错了吗?我只是想在数据网格中返回一些数据以只读方式显示。

我知道如果该工具不适合不要使用它,因此我可以在这里进行 SQL 调用,但我想了解为什么会有这样的差异。

下面是 LINQ,然后是它转储的 SQL。

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

还有 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

【问题讨论】:

    标签: .net sql linq sql-server-2005 linq-to-sql


    【解决方案1】:

    通过添加AsEnumerable():

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

    您正在强制完全评估第一个查询,并从数据库中获取每条记录 (AllEnrollees)。

    使用您的 SQL 语句,您将在服务器上进行所有过滤,这将快得多。

    【讨论】:

    • 我明白了,我应该发布第二个关于如何以正确方式修改我的 LINQ 语句的问题吗?
    • @Refracted:可能。您可以尝试仅删除“AsEnumerable”并查看您的提供者是否可以从查询中获得意义 - 这可能就是这里所必需的。
    • 我删除了第二部分中的AsEnumerable() 和两个let。可悲的是,它现在比以前运行得更慢了!
    【解决方案2】:

    首先,我不认为你是在比较苹果和苹果,你有很多 BllConnect.Something 围绕第二个 linq 查询的 select 调用。此外,您必须按照其他答案中的说明取出 AsE​​numerable。

    考虑以下(假设您在 db 和/或 linq2sql 设计器中添加了对应关系):

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

    【讨论】:

    • 感谢您的帮助。你是对的,BLLConnect.MemberLastName() 在你的帖子中造成了问题,说没有对应的 SQL。另外,我不确定你想用 from worker in workerGroup.DefaultIfEmpty() 向我展示什么
    • 刚刚删掉了from worker in workerGroup.DefaultIfEmpty() / 就在那里。 Re BllConnect.MemberLastName(),当你取出 AsE​​numerable 时,这就是你应该在你的版本中得到的。这正是问题所在,通过使用 AsEnumerable 或您可能正在执行的任何等效操作,您会导致部分 linq 查询在服务器上执行。
    • 我建议将 BLLConnect.MemberLastName 替换为其内联等效项,以便 linq2sql 在 sql 端执行此操作。此外,请确保您以后进行的任何调用都不会再次访问任何外部资源/如数据库,因为这些调用将针对每一行进行调用。
    • 谢谢,这是有道理的。最后一件事,您能否稍微详细说明一下“内联等效”的含义?谢谢。
    • 将方法中的代码移动到 linq 查询/如果可能的话,不确定你在里面做什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-01
    • 2013-06-05
    相关资源
    最近更新 更多