Улучшение SQL-запроса, генерируемого LINQ to EF - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть таблица учеников, и я хочу написать запрос Linq для получения нескольких подсчетов. SQL-запрос, сгенерированный Linq, слишком сложен и не оптимизирован.

Ниже приведено определение моей таблицы:

[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](100) NULL,
[Age] [int] NULL,

Мне нужно получить один счет для студентов с именем = test и один счет для студентов с возрастом> 10. Это один из запросов, которые я пробовал:

 var sql = from st in school.Students
                          group st by 1 into grp
                          select new
                          {
                              NameCount = grp.Count(k => k.Name == "Test"),
                              AgeCount = grp.Count(k => k.Age > 5)
                          };

Сгенерированный SQL-запрос:

SELECT 
    [Limit1].[C1] AS [C1], 
    [Limit1].[C2] AS [C2], 
    [Limit1].[C3] AS [C3]
    FROM ( SELECT TOP (1) 
        [Project2].[C1] AS [C1], 
        [Project2].[C2] AS [C2], 
        (SELECT 
            COUNT(1) AS [A1]
            FROM [dbo].[Student] AS [Extent3]
            WHERE ([Project2].[C1] = 1) AND ([Extent3].[Age] > 5)) AS [C3]
        FROM ( SELECT 
            [Distinct1].[C1] AS [C1], 
            (SELECT 
                COUNT(1) AS [A1]
                FROM [dbo].[Student] AS [Extent2]
                WHERE ([Distinct1].[C1] = 1) AND (N'Test' = [Extent2].[Name])) AS [C2]
            FROM ( SELECT DISTINCT 
                1 AS [C1]
                FROM [dbo].[Student] AS [Extent1]
            )  AS [Distinct1]
        )  AS [Project2]
    )  AS [Limit1]

Для меня это кажется сложным. Это может быть достигнуто следующим простым запросом:

select COUNT(CASE WHEN st.Name = 'Test' THEN 1 ELSE 0 END) NameCount,
 COUNT(CASE WHEN st.Age > 5 THEN 1 ELSE 0 END) AgeCount from Student st

Есть ли способ в LINQ, при котором генерируемый SQL-запрос будет иметь агрегацию, а не объединять два отдельных запроса с вложенными запросами?

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

Гораздо более простой запрос получается из-за того, что вы не используете ненужную группу и просто дважды запрашиваете таблицу в select:

var sql = from st in school.Students.Take(1)
          select new {
              NameCount = school.Students.Count(k => k.Name == "Test"),
              AgeCount = school.Students.Count(k => k.Age > 5)
          };
0 голосов
/ 30 апреля 2018

Из моего опыта работы с EF6, условное Sum (т. Е. Sum(condition ? 1 : 0)) переводится намного лучше в SQL, чем Count с предикатом (т. Е. Count(condition)):

var query =
    from st in school.Students
    group st by 1 into grp
    select new
    {
        NameCount = grp.Sum(k => k.Name == "Test" ? 1 : 0),
        AgeCount = grp.Sum(k => k.Age > 5 ? 1 : 0)
    };

Кстати, ваш пример SQL также должен использовать SUM. Чтобы использовать SQL COUNT, который исключает NULL с, он должен быть ELSE NULL или не ELSE:

select COUNT(CASE WHEN st.Name = 'Test' THEN 1 END) NameCount,
    COUNT(CASE WHEN st.Age > 5 THEN 1 END) AgeCount
    from Student st

Но для этого не существует эквивалентной конструкции LINQ, поэтому EF6 не может генерировать такой перевод. Но ИМО SUM является достаточно хорошим эквивалентом.

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