与创建自己的 SQL 视图相比,LINQ 生成的查询速度慢得可怕



>我用 LINQ 创建了以下查询:

var today = DateTime.Today;    
var month = new DateTime(today.Year, today.Month, 1);
var first = month.AddMonths(-1);
var last = month.AddDays(-1);
var baseQuery = (from jr in DollarU_JobRuns
                 where (jr.Day >= first && jr.Day <= last)
                 group jr by new { System = jr.SYSU.Substring(0, 2) }
                    into systemGroup
                 let NbrOfJobRunsSucceeded = systemGroup.Sum(x => x.NbrOfJobRunsSucceeded)
                 let NbrOfJobRunsFailed = systemGroup.Sum(s => s.NbrOfJobRunsFailed)
                 select new
                 {
                     System = systemGroup.Key.System,
                     NbrOfJobRunsSucceeded,
                     NbrOfJobRunsFailed,
                     Total = NbrOfJobRunsSucceeded + NbrOfJobRunsFailed
                 }).OrderByDescending(item => item.Total);
var query = baseQuery.Take(10).Concat(
                baseQuery.Skip(10).Select(item => new
                {
                    System = "Others",
                    NbrOfJobRunsSucceeded = item.NbrOfJobRunsSucceeded,
                    NbrOfJobRunsFailed = item.NbrOfJobRunsFailed,
                    Total = item.NbrOfJobRunsSucceeded + item.NbrOfJobRunsFailed,
                }
                )
            )
            .GroupBy(g => g.System)
            .Select(
                g => new 
                {
                    System = g.Key,
                    NbrOfJobRunsSucceeded = g.Sum(x => x.NbrOfJobRunsSucceeded),
                    NbrOfJobRunsFailed = g.Sum(x => x.NbrOfJobRunsFailed),
                    Total = g.Sum(x => x.Total)
                }
            )
            .OrderByDescending(item => item.Total);
query.Dump();

这将生成以下 SQL 查询:

SELECT 
    [Project9].[C5] AS [C1], 
    [Project9].[C4] AS [C2], 
    [Project9].[C1] AS [C3], 
    [Project9].[C2] AS [C4], 
    [Project9].[C3] AS [C5]
    FROM ( SELECT 
        [GroupBy5].[A1] AS [C1], 
        [GroupBy5].[A2] AS [C2], 
        [GroupBy5].[A3] AS [C3], 
        [GroupBy5].[K1] AS [C4], 
        1 AS [C5]
        FROM ( SELECT 
            [UnionAll1].[C1] AS [K1], 
            SUM([UnionAll1].[C2]) AS [A1], 
            SUM([UnionAll1].[C3]) AS [A2], 
            SUM([UnionAll1].[C4]) AS [A3]
            FROM  (SELECT TOP (10) 
                [Project3].[C2] AS [C1], 
                [Project3].[C1] AS [C2], 
                [Project3].[C4] AS [C3], 
                [Project3].[C3] AS [C4]
                FROM ( SELECT 
                    [Project2].[C1] AS [C1], 
                    [Project2].[C2] AS [C2], 
                    [Project2].[C1] + [Project2].[C3] AS [C3], 
                    [Project2].[C3] AS [C4]
                    FROM ( SELECT 
                        [Project1].[C1] AS [C1], 
                        [Project1].[C2] AS [C2], 
                        (SELECT 
                            SUM([Extent2].[NbrOfJobRunsFailed]) AS [A1]
                            FROM [dbo].[DollarU_JobRuns] AS [Extent2]
                            WHERE ([Extent2].[Day] >= '2017-09-01') AND ([Extent2].[Day] <= '2017-09-30') AND (([Project1].[C2] = (SUBSTRING([Extent2].[SYSU], 0 + 1, 2))) OR (([Project1].[C2] IS NULL) AND (SUBSTRING([Extent2].[SYSU], 0 + 1, 2) IS NULL)))) AS [C3]
                        FROM ( SELECT 
                            [GroupBy1].[A1] AS [C1], 
                            [GroupBy1].[K1] AS [C2]
                            FROM ( SELECT 
                                [Filter1].[K1] AS [K1], 
                                SUM([Filter1].[A1]) AS [A1]
                                FROM ( SELECT 
                                    SUBSTRING([Extent1].[SYSU], 0 + 1, 2) AS [K1], 
                                    [Extent1].[NbrOfJobRunsSucceeded] AS [A1]
                                    FROM [dbo].[DollarU_JobRuns] AS [Extent1]
                                    WHERE ([Extent1].[Day] >= '2017-09-01') AND ([Extent1].[Day] <= '2017-09-30')
                                )  AS [Filter1]
                                GROUP BY [K1]
                            )  AS [GroupBy1]
                        )  AS [Project1]
                    )  AS [Project2]
                )  AS [Project3]
                ORDER BY [Project3].[C3] DESC
            UNION ALL
                SELECT 
                [Project7].[C3] AS [C1], 
                [Project7].[C1] AS [C2], 
                [Project7].[C5] AS [C3], 
                [Project7].[C4] AS [C4]
                FROM ( SELECT 
                    [Project6].[C1] AS [C1], 
                    [Project6].[C1] + [Project6].[C2] AS [C2], 
                    N'Others' AS [C3], 
                    [Project6].[C1] + [Project6].[C2] AS [C4], 
                    [Project6].[C2] AS [C5]
                    FROM ( SELECT 
                        [Project5].[C1] AS [C1], 
                        (SELECT 
                            SUM([Extent4].[NbrOfJobRunsFailed]) AS [A1]
                            FROM [dbo].[DollarU_JobRuns] AS [Extent4]
                            WHERE ([Extent4].[Day] >= '2017-09-01') AND ([Extent4].[Day] <= '2017-09-30') AND (([Project5].[C2] = (SUBSTRING([Extent4].[SYSU], 0 + 1, 2))) OR (([Project5].[C2] IS NULL) AND (SUBSTRING([Extent4].[SYSU], 0 + 1, 2) IS NULL)))) AS [C2]
                        FROM ( SELECT 
                            [GroupBy3].[A1] AS [C1], 
                            [GroupBy3].[K1] AS [C2]
                            FROM ( SELECT 
                                [Filter3].[K1] AS [K1], 
                                SUM([Filter3].[A1]) AS [A1]
                                FROM ( SELECT 
                                    SUBSTRING([Extent3].[SYSU], 0 + 1, 2) AS [K1], 
                                    [Extent3].[NbrOfJobRunsSucceeded] AS [A1]
                                    FROM [dbo].[DollarU_JobRuns] AS [Extent3]
                                    WHERE ([Extent3].[Day] >= '2017-09-01') AND ([Extent3].[Day] <= '2017-09-30')
                                )  AS [Filter3]
                                GROUP BY [K1]
                            )  AS [GroupBy3]
                        )  AS [Project5]
                    )  AS [Project6]
                )  AS [Project7]
                ORDER BY [Project7].[C2] DESC
                OFFSET 10 ROWS ) AS [UnionAll1]
            GROUP BY [UnionAll1].[C1]
        )  AS [GroupBy5]
    )  AS [Project9]
    ORDER BY [Project9].[C3] DESC

我还创建了一个SQL视图,它为我提供了相同的结果:

With TopItems As
(
    SELECT SUBSTRING( [SYSU],1,2) AS [System]
            , Sum([NbrOfJobRunsSucceeded]) AS NbrOfJobRunsSucceeded
            , Sum([NbrOfJobRunsFailed]) AS NbrOfJobRunsFailed
            , Sum([NbrOfJobRunsSucceeded] + [NbrOfJobRunsFailed]) AS Total
            , ROW_NUMBER() OVER (ORDER BY Sum([NbrOfJobRunsSucceeded] + [NbrOfJobRunsFailed]) DESC) AS Num
    FROM [dbo].[DollarU_JobRuns]    
    WHERE  [Day] >= DATEADD(month, -1, DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0))
            AND [Day] < DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
    GROUP BY SUBSTRING( [SYSU],1,2)

)
SELECT [System], NbrOfJobRunsSucceeded, NbrOfJobRunsFailed, Total
FROM TopItems
WHERE Num <= 10
UNION ALL
SELECT 
    'Others'
    , Sum([NbrOfJobRunsSucceeded]) AS NbrOfJobRunsSucceeded
    , Sum([NbrOfJobRunsFailed]) AS NbrOfJobRunsFailed   
    , Sum(Total)
FROM TopItems
WHERE Num > 10

首先比较结果时,我发现我自己精心制作的SQL视图更简洁。但是我喜欢创建 LINQ 语句,它也非常简洁,但生成的 SQL 查询非常大且无法理解。但是无论产生什么(无论如何都在引擎盖下)只要性能正常就没有那么重要。但 SQL 视图的执行时间为 27.5 毫秒,比 LINQ 生成的查询的 120.3 毫秒快得多。

LINQ 使用起来很糟糕,还是我错过了一些东西来提高它的性能?

看起来使用组聚合的 2 个let语句的 EF 查询翻译存在缺陷。您可以尝试改用多重投影:

var baseQuery = DollarU_JobRuns
    .Where(jr => jr.Day >= first && jr.Day <= last)
    .GroupBy(jr => new { System = jr.SYSU.Substring(0, 2) })
    .Select(systemGroup => new
    {
        System = systemGroup.Key.System,
        NbrOfJobRunsSucceeded = systemGroup.Sum(x => x.NbrOfJobRunsSucceeded),
        NbrOfJobRunsFailed = systemGroup.Sum(x => x.NbrOfJobRunsFailed),
    })
    .Select(item => new
    {
        item.System,
        item.NbrOfJobRunsSucceeded,
        item.NbrOfJobRunsFailed,
        Total = item.NbrOfJobRunsFailed + item.NbrOfJobRunsFailed
    })
    .OrderByDescending(item => item.Total);

因此,最终查询现在转换为:

SELECT
    [Project5].[C5] AS [C1],
    [Project5].[C4] AS [C2],
    [Project5].[C1] AS [C3],
    [Project5].[C2] AS [C4],
    [Project5].[C3] AS [C5]
    FROM ( SELECT
        [GroupBy3].[A1] AS [C1],
        [GroupBy3].[A2] AS [C2],
        [GroupBy3].[A3] AS [C3],
        [GroupBy3].[K1] AS [C4],
        1 AS [C5]
        FROM ( SELECT
            [UnionAll1].[C1] AS [K1],
            SUM([UnionAll1].[C2]) AS [A1],
            SUM([UnionAll1].[C3]) AS [A2],
            SUM([UnionAll1].[C4]) AS [A3]
            FROM  (SELECT TOP (10)
                [Project1].[C3] AS [C1],
                [Project1].[C1] AS [C2],
                [Project1].[C2] AS [C3],
                [Project1].[C4] AS [C4]
                FROM ( SELECT
                    [GroupBy1].[A1] AS [C1],
                    [GroupBy1].[A2] AS [C2],
                    [GroupBy1].[K1] AS [C3],
                    [GroupBy1].[A2] + [GroupBy1].[A2] AS [C4]
                    FROM ( SELECT
                        [Filter1].[K1] AS [K1],
                        SUM([Filter1].[A1]) AS [A1],
                        SUM([Filter1].[A2]) AS [A2]
                        FROM ( SELECT
                            SUBSTRING([Extent1].[SYSU], 0 + 1, 2) AS [K1],
                            [Extent1].[NbrOfJobRunsSucceeded] AS [A1],
                            [Extent1].[NbrOfJobRunsFailed] AS [A2]
                            FROM [dbo].[DollarU_JobRuns] AS [Extent1]
                            WHERE ([Extent1].[Day] >= @p__linq__0) AND ([Extent1].[Day] <= @p__linq__1)
                        )  AS [Filter1]
                        GROUP BY [K1]
                    )  AS [GroupBy1]
                )  AS [Project1]
                ORDER BY [Project1].[C4] DESC
            UNION ALL
                SELECT
                [Project3].[C4] AS [C1],
                [Project3].[C1] AS [C2],
                [Project3].[C2] AS [C3],
                [Project3].[C5] AS [C4]
                FROM ( SELECT
                    [GroupBy2].[A1] AS [C1],
                    [GroupBy2].[A2] AS [C2],
                    [GroupBy2].[A2] + [GroupBy2].[A2] AS [C3],
                    N'Others' AS [C4],
                    [GroupBy2].[A1] + [GroupBy2].[A2] AS [C5]
                    FROM ( SELECT
                        [Filter2].[K1] AS [K1],
                        SUM([Filter2].[A1]) AS [A1],
                        SUM([Filter2].[A2]) AS [A2]
                        FROM ( SELECT
                            SUBSTRING([Extent2].[SYSU], 0 + 1, 2) AS [K1],
                            [Extent2].[NbrOfJobRunsSucceeded] AS [A1],
                            [Extent2].[NbrOfJobRunsFailed] AS [A2]
                            FROM [dbo].[DollarU_JobRuns] AS [Extent2]
                            WHERE ([Extent2].[Day] >= @p__linq__2) AND ([Extent2].[Day] <= @p__linq__3)
                        )  AS [Filter2]
                        GROUP BY [K1]
                    )  AS [GroupBy2]
                )  AS [Project3]
                ORDER BY [Project3].[C3] DESC
                OFFSET 10 ROWS ) AS [UnionAll1]
            GROUP BY [UnionAll1].[C1]
        )  AS [GroupBy3]
    )  AS [Project5]
    ORDER BY [Project5].[C3] DESC

建议您创建一个更接近业务域的视图,并考虑在 EF 模型中引用该视图。

最新更新