【问题标题】:LINQ - Need Help Optimizing EXISTSLINQ - 需要帮助优化 EXISTS
【发布时间】:2018-02-01 20:07:07
【问题描述】:

更新:当前 linq 代码添加到底部,寻找建议以删除 linq 在创建错误执行计划时自动添加的子查询

我有一个运行时间超过 25 秒的查询,但如果我可以在 EXISTS 语句中向上移动一个条件,它将在几毫秒内运行。

我希望有人能帮助我在 LINQ 中生成所需的执行计划。

LINQ 产生的 SQL:

SELECT TOP (50) 
[Filter3].[id] AS [id], 
[Filter3].[client_id] AS [client_id], 
[Filter3].[status] AS [status], 
[Filter3].[name1] AS [name], 
[Filter3].[C1] AS [C1], 
[Filter3].[fail_alert_ind] AS [fail_alert_ind], 
[Filter3].[parent_group_exec_id] AS [parent_group_exec_id], 
[Filter3].[C2] AS [C2], 
[Filter3].[id1] AS [id1], 
[Filter3].[case_group_ind] AS [case_group_ind], 
[Filter3].[C3] AS [C3], 
[Filter3].[execution_ratio] AS [execution_ratio], 
[Filter3].[name] AS [name1], 
[Filter3].[name3] AS [name2], 
[Filter3].[status_color] AS [status_color], 
[Filter3].[scheduled_start_time] AS [scheduled_start_time], 
[Filter3].[C4] AS [C4], 
[Filter3].[C5] AS [C5], 
[Filter3].[name4] AS [name3], 
[Filter3].[name2] AS [name4], 
[Filter3].[C6] AS [C6], 
[Filter3].[upload_ratio] AS [upload_ratio], 
[Filter3].[id2] AS [id2], 
[Filter3].[C7] AS [C7], 
[Filter3].[hold_ind] AS [hold_ind], 
[Filter3].[C8] AS [C8]
FROM ( SELECT [Project1].[id] AS [id], [Project1].[scheduled_start_time] AS [scheduled_start_time], [Project1].[parent_group_exec_id] AS [parent_group_exec_id], [Project1].[execution_ratio] AS [execution_ratio], [Project1].[hold_ind] AS [hold_ind], [Project1].[upload_ratio] AS [upload_ratio], [Project1].[name] AS [name], [Project1].[fail_alert_ind] AS [fail_alert_ind], [Project1].[id1] AS [id1], [Project1].[name1] AS [name1], [Project1].[case_group_ind] AS [case_group_ind], [Project1].[name2] AS [name2], [Project1].[client_id] AS [client_id], [Project1].[status] AS [status], [Project1].[id2] AS [id2], [Project1].[C1] AS [C1], [Project1].[C2] AS [C2], [Project1].[C3] AS [C3], [Project1].[C4] AS [C4], [Project1].[C5] AS [C5], [Project1].[C6] AS [C6], [Project1].[C7] AS [C7], [Project1].[C8] AS [C8], [Project1].[name3] AS [name3], [Project1].[status_color] AS [status_color], [Project1].[name4] AS [name4], row_number() OVER (ORDER BY [Project1].[id] ASC) AS [row_number]
    FROM ( SELECT 
        [Extent1].[id] AS [id], 
        [Extent1].[scheduled_start_time] AS [scheduled_start_time], 
        [Extent1].[parent_group_exec_id] AS [parent_group_exec_id], 
        [Extent1].[execution_ratio] AS [execution_ratio], 
        [Extent1].[hold_ind] AS [hold_ind], 
        [Extent1].[upload_ratio] AS [upload_ratio], 
        [Extent2].[name] AS [name], 
        [Extent2].[fail_alert_ind] AS [fail_alert_ind], 
        [Extent3].[id] AS [id1], 
        [Extent3].[name] AS [name1], 
        [Extent3].[case_group_ind] AS [case_group_ind], 
        [Extent4].[name] AS [name2], 
        [Extent4].[client_id] AS [client_id], 
        [Extent4].[status] AS [status], 
        [Extent6].[id] AS [id2], 
        [Extent5].[first_name] + N' ' + [Extent5].[last_name] AS [C1], 
        CASE WHEN ([Extent1].[patriarch_id] IS NULL) THEN 0 ELSE [Extent1].[patriarch_id] END AS [C2], 
        N'Group' AS [C3], 
        [Extent8].[first_name] + N' ' + [Extent8].[last_name] AS [C4], 
        [Extent9].[first_name] + N' ' + [Extent9].[last_name] AS [C5], 
        CASE WHEN (1 = [Extent1].[uploaded]) THEN N'Yes' ELSE N'No' END AS [C6], 
        CASE WHEN ([Extent3].[external_test_mgmt_id] IS NOT NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C7], 
        CASE WHEN (1 = [Extent6].[accept_revisions_ind]) THEN 1 ELSE 0 END AS [C8], 
        [Extent7].[name] AS [name3], 
        [Extent7].[status_color] AS [status_color], 
        [Extent10].[name] AS [name4]
        FROM            [dbo].[group_execution] AS [Extent1]
        INNER JOIN [dbo].[automation_sequence_status] AS [Extent2] ON [Extent1].[run_status_id] = [Extent2].[id]
        INNER JOIN [dbo].[automation_sequences] AS [Extent3] ON [Extent1].[automation_sequence_id] = [Extent3].[id]
        INNER JOIN [dbo].[project] AS [Extent4] ON [Extent3].[project_id] = [Extent4].[id]
        INNER JOIN [dbo].[users] AS [Extent5] ON [Extent3].[last_modified_by_id] = [Extent5].[id]
        INNER JOIN [dbo].[execution_schedule] AS [Extent6] ON [Extent1].[schedule_id] = [Extent6].[id]
        LEFT OUTER JOIN [dbo].[automation_sequence_test_case_status] AS [Extent7] ON [Extent1].[runtime_case_grp_status] = [Extent7].[id]
        LEFT OUTER JOIN [dbo].[users] AS [Extent8] ON [Extent1].[executed_by_id] = [Extent8].[id]
        LEFT OUTER JOIN [dbo].[users] AS [Extent9] ON [Extent3].[created_by_id] = [Extent9].[id]
        LEFT OUTER JOIN [dbo].[machines] AS [Extent10] ON [Extent1].[machine_id] = [Extent10].[id]
        LEFT OUTER JOIN [dbo].[automation_sequence_executions] AS [Extent11] ON [Extent1].[holding_at_id] = [Extent11].[id]
        LEFT OUTER JOIN [dbo].[group_execution] AS [Extent12] ON [Extent1].[holding_at_id] = [Extent12].[id]
    )  AS [Project1]
    WHERE ((1 = [Project1].[client_id]) AND (1 = [Project1].[status]) AND ([Project1].[parent_group_exec_id] IS NULL) AND ([Project1].[name1] LIKE '%UAT%')) OR ( EXISTS (SELECT 
        1 AS [C1]
        FROM   (SELECT [Extent13].[id] AS [id3], [Extent13].[patriarch_id] AS [patriarch_id], [Extent13].[runtime_case_grp_status] AS [runtime_case_grp_status]
            FROM  [dbo].[automation_sequence_executions] AS [Extent13]
            INNER JOIN [dbo].[automation_sequences] AS [Extent14] ON [Extent13].[automation_sequence_id] = [Extent14].[id]
            WHERE [Extent14].[last_modified_by_id] IS NOT NULL ) AS [Filter1]
        LEFT OUTER JOIN [dbo].[automation_sequence_test_case_status] AS [Extent15] ON [Filter1].[runtime_case_grp_status] = [Extent15].[id]
        WHERE ((CASE WHEN ([Filter1].[patriarch_id] IS NULL) THEN [Filter1].[id3] ELSE [Filter1].[patriarch_id] END) = [Project1].[C2]) AND ((CASE WHEN ([Filter1].[patriarch_id] IS NULL) THEN [Filter1].[id3] ELSE [Filter1].[patriarch_id] END) = [Project1].[C2])
    ))
)  AS [Filter3]
WHERE [Filter3].[row_number] > 0
ORDER BY [Filter3].[id] ASC

EXISTS 中的 Where 子句是我所有问题的来源。它将 where 子句放在它正在生成的这个新子查询之外。

上面的例子:

WHERE ((CASE WHEN ([Filter1].[patriarch_id] IS NULL) THEN [Filter1].[id3] ELSE [Filter1].[patriarch_id] END) = [Project1].[C2]) AND ((CASE WHEN ([Filter1].[patriarch_id] IS NULL) THEN [Filter1].[id3] ELSE [Filter1].[patriarch_id] END) = [Project1].[C2])

我希望 EXISTS 看起来像这样:

注意 [Project1].[C2] 的检查是如何上移的。

WHERE ((1 = [Project1].[client_id]) AND (1 = [Project1].[status]) AND ([Project1].[parent_group_exec_id] IS NULL) AND ([Project1].[name1] LIKE '%UAT%')) OR ( EXISTS (SELECT 
    1 AS [C1]
    FROM   (SELECT [Extent13].[id] AS [id3], [Extent13].[patriarch_id] AS [patriarch_id], [Extent13].[runtime_case_grp_status] AS [runtime_case_grp_status]
        FROM  [dbo].[automation_sequence_executions] AS [Extent13]
        INNER JOIN [dbo].[automation_sequences] AS [Extent14] ON [Extent13].[automation_sequence_id] = [Extent14].[id]
        WHERE [Extent14].[last_modified_by_id] IS NOT NULL 
            and 
            ([Extent13].[patriarch_id] = [Project1].[C2])
            ) AS [Filter1]
    LEFT OUTER JOIN [dbo].[automation_sequence_test_case_status] AS [Extent15] ON [Filter1].[runtime_case_grp_status] = [Extent15].[id]
))

当前的 LINQ 代码

如果有人有任何建议以防止创建子查询,我们将不胜感激。

谢谢!

void Main()
{
    Expression<Func<Contract_SeqExecution, bool>> globalFilter = r => r.ModifiedBy.Contains("w");
    Contract_SeqExecution c = new Contract_SeqExecution();

    //query root
    Expression<Func<Contract_SeqExecution, bool>> rootFilter = x => x.client_id == 1 && x.project_status == true && x.parentId == null;
    rootFilter = rootFilter.And(globalFilter);
    IQueryable<Contract_SeqExecution> geRootResults = c.queryGroups(this);
    IQueryable<Contract_SeqExecution> aseRootResults = c.queryTestCases(this);

    var rootUnion = geRootResults.Concat(aseRootResults);
    //end query root

    //query descendants
    Expression<Func<Contract_SeqExecution, bool>> descendantFilter = x => x.client_id == 1 && x.project_status == true && x.parentId != null;
    descendantFilter = descendantFilter.And(globalFilter);
    IQueryable<Contract_SeqExecution> geDescendantResults = c.queryGroups(this);
    IQueryable<Contract_SeqExecution> aseDescendantResults = c.queryTestCases(this);

    var descendantUnion = geDescendantResults.Concat(aseDescendantResults);
    //end query descendants

    //Perform the EXISTS statement in where clause.  This is the problem code
    Expression<Func<Contract_SeqExecution, bool>> childFilter = r => descendantUnion.Where(descendantFilter).Any(x=>x.patriarchId == r.ID);

    rootFilter = rootFilter.Or(childFilter);
    rootUnion = rootUnion.AsExpandable().Where(rootFilter);
    rootUnion.OrderBy(r => r.ID).Skip(0).Take(50).Dump();
}

public class Contract_SeqExecution
{
    public int ID { get; set; }   
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public int SeqID { get; set; }
    public int CaseGroupInd { get; set; }
    public string CaseGroupText { get; set; }
    public string ExecRatio { get; set; }
    public string ModifiedBy { get; set; }
    public string Project { get; set; }
    public string Uploaded { get; set; }
    public string UploadRatio { get; set; }
    public bool FailedInd { get; set; }
    public bool HoldInd { get; set; }
    public int? parentId { get; set; }
    public int patriarchId { get; set; }
    public DateTime? SchedRunTime { get; set; }

    //other fields
    public string Machine {get;set;}
    public int is_accepting_changes {get;set;}
    public string holding_at_name {get;set;}
    public string RunStatus {get;set;}
    public string CaseGroupStatus {get; set;}
    public string TCStatusColor {get; set;}
    public string ExecutedBy {get;set;}
    public string CreatedBy {get;set;}
    public int? ScheduleID {get;set;}
    public bool SeqUploaded {get;set;}
    //end other fields

    public int? parent_group_exec_id { get; set; }
    public int client_id { get; set; }
    public bool project_status { get; set; }

    public IQueryable<Contract_SeqExecution> queryGroups(UserQuery context)
    {
            var result = (from ge in context.group_execution
                            join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
                            join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
                            join p in context.project on aseq.project_id equals p.id
                            join es in context.execution_schedule on ge.schedule_id equals es.id
                            join exe_u in context.users on ge.executed_by_id equals exe_u.id
                            join create_u in context.users on aseq.created_by_id equals create_u.id

                            join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
                            from modify_u in modify_uSub.DefaultIfEmpty()
                            join astcs in context.automation_sequence_test_case_status on ge.runtime_case_grp_status equals astcs.id into astcsSub
                            from astcs in astcsSub.DefaultIfEmpty()
                            join m in context.machines on ge.machine_id equals m.id into mSub
                            from m in mSub.DefaultIfEmpty()

                            join aseh in context.automation_sequence_executions on ge.holding_at_id equals aseh.id into asehSub
                            from aseh in asehSub.DefaultIfEmpty()
                            join geh in context.group_execution on ge.holding_at_id equals geh.id into gehSub
                            from geh in gehSub.DefaultIfEmpty()
                            select new Contract_SeqExecution
                            {
                                client_id = p.client_id,
                                project_status = p.status,
                                ID = ge.id,
                                Name = aseq.name,
                                ModifiedBy = modify_u.first_name + " " + modify_u.last_name,
                                FailedInd = asstatus.fail_alert_ind,
                                parentId = ge.parent_group_exec_id,
                                patriarchId = ge.patriarch_id ?? 0,

                                //other fields
                                SeqID = aseq.id,
                                CaseGroupInd = aseq.case_group_ind,
                                CaseGroupText = "Group",
                                ExecRatio = ge.execution_ratio,
                                RunStatus = asstatus.name,
                                CaseGroupStatus = astcs.name,
                                TCStatusColor = astcs.status_color, 
                                SchedRunTime = ge.scheduled_start_time,
                                ExecutedBy = exe_u.first_name + " " + exe_u.last_name,
                                CreatedBy = create_u.first_name + " " + create_u.last_name,
                                Machine = m.name,
                                Project = p.name,
                                //InheritIDs = SqlFunctions.StringConvert((double)ase.id),
                                Uploaded = (ge.uploaded == true ? "Yes" : "No"),
                                UploadRatio = ge.upload_ratio,
                                ScheduleID = es.id,
                                SeqUploaded = (aseq.external_test_mgmt_id != null ? true : false),
                                HoldInd = ge.hold_ind,
                            //holding_at_id = ge.holding_at_id,
                            //holding_at_gih_id = null,
                            //holding_at_name = aseh.hold_name ?? geh.hold_name,
                            //contains_holds = ge.contains_holds
                                is_accepting_changes = (es.accept_revisions_ind == 1 ? 1 : 0)
                                //end other fields
                            });
            return result;
    }

    public IQueryable<Contract_SeqExecution> queryTestCases(UserQuery context)
    {
        var result = (from ase in context.automation_sequence_executions
                        join asstatus in context.automation_sequence_status on ase.automation_sequence_status_id equals asstatus.id
                        join aseq in context.automation_sequences on ase.automation_sequence_id equals aseq.id
                        join p in context.project on aseq.project_id equals p.id
                        join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id

                        join astcs in context.automation_sequence_test_case_status on ase.runtime_case_grp_status equals astcs.id into astcsSub
                        from astcs in astcsSub.DefaultIfEmpty()
                        join exe_u in context.users on ase.executed_by_id equals exe_u.id into exe_uSub
                        from exe_u in exe_uSub.DefaultIfEmpty()
                        join create_u in context.users on aseq.created_by_id equals create_u.id into create_uSub
                        from create_u in create_uSub.DefaultIfEmpty()
                        join m in context.machines on ase.machine_id equals m.id into mSub
                        from m in mSub.DefaultIfEmpty()
                        join es in context.execution_schedule on ase.schedule_id equals es.id into esSub
                        from es in esSub.DefaultIfEmpty()
                        select new Contract_SeqExecution
                        {
                            client_id = p.client_id,
                            project_status = p.status,
                            ID = ase.id,
                            Name = aseq.name,
                            ModifiedBy = modify_u.first_name + " " + modify_u.last_name,
                            FailedInd = asstatus.fail_alert_ind,
                            parentId = ase.parent_group_exec_id,
                            patriarchId = ase.patriarch_id ?? ase.id,

                            //other fields
                            SeqID = aseq.id,
                            CaseGroupInd = aseq.case_group_ind,
                            CaseGroupText = "Test Case",
                            ExecRatio = (asstatus.complete_ind == true && asstatus.id != 23 ? "1/1" : "0/1"),
                            RunStatus = asstatus.name,
                            CaseGroupStatus = astcs.name,
                            TCStatusColor = astcs.status_color, 
                            SchedRunTime = ase.execution_start_time,
                            ExecutedBy = exe_u.first_name + " " + exe_u.last_name,
                            CreatedBy = create_u.first_name + " " + create_u.last_name,
                            Machine = m.name,
                            Project = p.name,
                        //InheritIDs = SqlFunctions.StringConvert((double)ase.id),
                            Uploaded = (ase.external_test_mgmt_id != null ? "Yes" : "No"),
                            UploadRatio = (ase.external_test_mgmt_id != null ? "1/1" : "0/1"),
                            ScheduleID = ase.schedule_id,
                            SeqUploaded = (aseq.external_test_mgmt_id != null ? true : false),
                            HoldInd = asstatus.hold_alert_ind,
                        //holding_at_id = ge.holding_at_id,
                        //holding_at_gih_id = null,
                        //holding_at_name = aseh.hold_name ?? geh.hold_name,
                        //contains_holds = ge.contains_holds
                            is_accepting_changes = (es.accept_revisions_ind == 1 ? 1 : 0)
                            //end other fields
                        });
            return result;
    }
}

【问题讨论】:

  • 您当前的 LINQ 代码是什么?
  • 将 linq 分成更小的部分应该更容易获得您想要的结果。
  • @stephen.vakil 我已将代码添加到问题的底部。感谢您花时间查看它。
  • where 子句是创建子查询并破坏性能的原因。为什么 where 子句会导致 linq 创建子查询,有什么方法可以抑制这种行为,或者它是否以某种方式脱离了数据模式?

标签: c# asp.net entity-framework linq linq-to-entities


【解决方案1】:

如果将Where 条件descendantFilter 合并到Any 中会怎样?

Expression<Func<Contract_SeqExecution, bool>> childFilter = r => descendantUnion.Any(descendantFilter.And(x=>x.patriarchId == r.ID));

【讨论】:

  • 很酷的想法,但它会引发错误:Internal .NET Framework Data Provider error 1025。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多