C# 将LINQ生成的SQL查询改进为EF

C# 将LINQ生成的SQL查询改进为EF,c#,sql-server,entity-framework,linq,C#,Sql Server,Entity Framework,Linq,我有一个学生表,我想写一个Linq查询来获得多个计数。Linq生成的SQL查询太复杂且未优化 以下是我的表格的定义: [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](100) NULL, [Age] [int] NULL, 我需要对name=test的学生进行一次计数,对年龄>10岁的学生进行一次计数。 这是我尝试过的查询之一: var sql = from st in school.Students

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

以下是我的表格的定义:

[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](100) NULL,
[Age] [int] NULL,
我需要对name=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查询同时具有聚合,而不是将两个单独的查询与嵌套查询连接起来?

根据我使用EF6的经验,条件和即Sumcondition?1:0转换为SQL比使用谓词Count(即Countcondition:

顺便说一句,您的SQL示例也应该使用SUM。为了利用排除NULL的SQL计数,它应该是ELSE NULL或no-ELSE:


但是没有等效的LINQ结构,因此无法让EF6生成这样的翻译。但在我看来,这个总和已经足够好了。

根据我对EF6的经验,有条件的总和,即Sumcondition?1:0转换为SQL比使用谓词Count(即Countcondition:

顺便说一句,您的SQL示例也应该使用SUM。为了利用排除NULL的SQL计数,它应该是ELSE NULL或no-ELSE:


但是没有等效的LINQ结构,因此无法让EF6生成这样的翻译。但是在我看来,总和相当不错。

一个简单得多的查询结果是不使用不必要的group by,只需在select中查询两次表:


不使用不必要的group by,只需在select中查询两次表,即可实现更简单的查询:


根据我的经验,我可以说EF不会为每个场景生成优化查询。因此,您可以在这里使用EF框架直接调用优化的查询并获得结果。您还可以编写一个存储过程来缓存执行计划,以便更快地得到结果。您是否实际比较了执行计划,以查看EF查询是否效率低下?仅仅因为它更长并不一定意味着它更糟。我的测试似乎表明,SQL中的COUNTCASE将计算所有行,而不仅仅是返回1的行。根据我的经验,我可以说EF不会为每个场景生成优化查询。因此,您可以在这里使用EF框架直接调用优化的查询并获得结果。您还可以编写一个存储过程来缓存执行计划,以便更快地得到结果。您是否实际比较了执行计划,以查看EF查询是否效率低下?我的测试似乎表明,SQL中的COUNTCASE将计算所有行,而不仅仅是返回1.np的行。我在EF中没有注意到这种行为。我相信我会很快使用它+1从我这里。@Evk按照它的方式,它将计算正确和错误的条件。COUNT1与COUNT0以及COUNT*np相同。我在EF中没有注意到这种行为。我相信我会很快使用它+1从我这里。@Evk按照它的方式,它将计算正确和错误的条件。COUNT1与COUNT0以及COUNT相同*
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)
    };
select COUNT(CASE WHEN st.Name = 'Test' THEN 1 END) NameCount,
    COUNT(CASE WHEN st.Age > 5 THEN 1 END) AgeCount
    from Student st
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)
          };