【问题标题】:Improve performance in LINQ To Entities query (aggregation)提高 LINQ To Entities 查询(聚合)的性能
【发布时间】:2019-02-04 19:27:09
【问题描述】:

我有这个 EF 模型:

class Reception
{
     public string Code { get; set; }
     public virtual List<Row> { get; set; }
}

class Row 
{
     public string Item { get; set; }
     public int Quantity { get; set; }
     public float Weight { get; set; }
}

有没有办法改进以下 LINQ To Entities 查询?

dbContext.Receptions.Select(r => new 
{
     code = r.Code,
     quantitySum = r.Rows.Sum(e => e.Quantity),
     weightSum = r.Rows.Sum(e => e.Weight),
});

我担心做两次“r.Rows”部分。

我不应该担心吗?

【问题讨论】:

    标签: entity-framework linq-to-entities


    【解决方案1】:

    如果基于 GroupBy,聚合查询(尤其是包含多个聚合的查询)可以更好地转换为 SQL,因为这是聚合的自然 SQL 构造。

    因此,如果您想要更好的翻译并忍受代码可读性,可以将有问题的查询转换为left outer join + group by,如下所示:

    var query = dbContext.Receptions
        .SelectMany(r => r.Rows.DefaultIfEmpty(), (r, e) => new
        {
            r.Code,
            Quantity = (int?)e.Quantity ?? 0,
            Weight = (float?)e.Weight ?? 0,
        })
        .GroupBy(e => e.Code, (key, g) => new
        {
            code = key,
            quantitySum = g.Sum(e => e.Quantity),
            weightSum = g.Sum(e => e.Weight),
        });
    

    翻译成这样的东西

    SELECT 
        1 AS [C1], 
        [GroupBy1].[K1] AS [Code], 
        [GroupBy1].[A1] AS [C2], 
         CAST( [GroupBy1].[A2] AS real) AS [C3]
        FROM ( SELECT 
            [Join1].[K1] AS [K1], 
            SUM([Join1].[A1]) AS [A1], 
            SUM([Join1].[A2]) AS [A2]
            FROM ( SELECT 
                [Extent1].[Code] AS [K1], 
                CASE WHEN ([Extent2].[Quantity] IS NULL) THEN 0 ELSE [Extent2].[Quantity] END AS [A1], 
                CASE WHEN ([Extent2].[Weight] IS NULL) THEN cast(0 as real) ELSE [Extent2].[Weight] END AS [A2]
                FROM  [dbo].[Receptions] AS [Extent1]
                LEFT OUTER JOIN [dbo].[Rows] AS [Extent2] ON [Extent1].[Code] = [Extent2].[Reception_Code]
            )  AS [Join1]
            GROUP BY [K1]
        )  AS [GroupBy1]
    

    对于此特定查询,您可以从 EF6 获得最好的结果。

    【讨论】:

    • 该死的......性能与代码可读性。我需要考虑一下
    • 是的 - 不幸的是:(
    【解决方案2】:

    通常,EF 应该将其转换为相当有效的数据库查询,同时在数据库端使用 SUM 函数。但如果您想确定,请使用您的 SQL Server Profiler 来分析查询。

    我为您快速运行了这段代码,这就是 EF 所做的:

    SELECT 
        [Project2].[Id] AS [Id], 
        [Project2].[Code] AS [Code], 
        [Project2].[C1] AS [C1], 
         CAST( [Project2].[C2] AS real) AS [C2]
    FROM 
        (SELECT 
            [Project1].[Id] AS [Id], 
            [Project1].[Code] AS [Code], 
            [Project1].[C1] AS [C1], 
            (SELECT SUM([Extent3].[Weight]) AS [A1] FROM [dbo].[Rows] AS [Extent3] WHERE [Project1].[Id] = [Extent3].[Reception_Id]) AS [C2]
        FROM 
            (SELECT 
                [Extent1].[Id] AS [Id], 
                [Extent1].[Code] AS [Code], 
                (SELECT SUM([Extent2].[Quantity]) AS [A1] FROM [dbo].[Rows] AS [Extent2] WHERE [Extent1].[Id] = [Extent2].[Reception_Id]) AS [C1]
            FROM 
                [dbo].[Receptions] AS [Extent1]
            )AS [Project1]
        )AS [Project2]
    

    当然,我们可以编写一个更好的查询,使用更少的子选择,但实际上,这个查询并不是 SQL Server 会遇到的。

    【讨论】:

    • 这显示了来自 dbo.Rows 的双重选择,根据我的经验 SQL Server 没有优化的东西以及来自该表的单一选择。
    • 此查询具有完全相同的执行计划:SELECT [Id], [Code], (SELECT SUM([Extent2].[Quantity]) AS [A1] FROM [dbo].[Rows] AS [Extent2] WHERE re.[Id] = [Extent2].[Reception_Id]) AS [C1], (SELECT SUM([Extent3].[Weight]) AS [A1] FROM [dbo].[Rows] AS [ Extent3] WHERE re.[Id] = [Extent3].[Reception_Id]) AS [C2] FROM [dbo].[Receptions] re
    • 但是加入和group by,还是可以赢一点的。
    猜你喜欢
    • 1970-01-01
    • 2020-05-06
    • 2019-01-25
    • 2011-05-24
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多