改进LINQ生成的SQL查询到EF

时间:2018-04-30 18:48:14

标签: c# sql-server entity-framework linq

我有学生表,我想写一个Linq查询来获取多个计数。 Linq生成的SQL查询过于复杂且未经优化。

以下是我的表的定义:

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

我需要给名字=考试的学生一个计数,并为年龄> gt的学生计算一个计数; 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 :(得分:5)

根据我使用EF6的经验,条件Sum(即Sum(condition ? 1 : 0))的翻译效果要比使用谓词Count的{​​{1}}更好地转换为SQL:

Count(condition)

顺便说一下,你的SQL示例也应该使用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) }; 。为了利用排除SUM s的SQL COUNT,它应该是NULL或者不是ELSE NULL

ELSE

但是没有相应的LINQ构造,因此没有办法让EF6生成这样的翻译。但IMO select COUNT(CASE WHEN st.Name = 'Test' THEN 1 END) NameCount, COUNT(CASE WHEN st.Age > 5 THEN 1 END) AgeCount from Student st 足够相当。

答案 1 :(得分:0)

更简单的查询是因为没有使用不必要的组,只是在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)
          };