LINQ - 需要帮助优化EXISTS

时间:2018-02-01 20:07:07

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

UPDATE:当前的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]为空)然后[Filter1]。[id3] ELSE [Filter1]。[patriarch_id] END)= [Project1]。[C2])AND ((CASE WHEN([Filter1]。[patriarch_id] IS NULL)那么[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]
))

CURRENT LINQ CODE

如果任何人有任何建议以防止创建子查询,那将是受欢迎的。

谢谢!

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

1 个答案:

答案 0 :(得分:0)

如果您将Where条件descendantFilter纳入Any会怎样?

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