Повышение производительности в запросе LINQ To Entities (агрегация) - PullRequest
0 голосов
/ 30 августа 2018

У меня есть эта модель 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" часть.

Разве я не должен волноваться?

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Агрегированные запросы (особенно те, которые содержат несколько агрегатов) лучше переводятся в SQL, если основаны на GroupBy, потому что это естественная конструкция 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 для этого конкретного запроса.

0 голосов
/ 30 августа 2018

Обычно 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 не отключит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...