简体   繁体   English

改善LINQ到EF生成的SQL查询

[英]Improving SQL query generated by LINQ to EF

I have student table and I want to write a Linq query to get multiple counts. 我有一个学生表,我想编写一个Linq查询来获取多个计数。 The SQL query that Linq generates is too complicated and un-optimized. Linq生成的SQL查询过于复杂且未经优化。

Following is definition of my table: 以下是我的表的定义:

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

I need to get one count with students with name = test and one count for students with age > 10. This is one of the query I have tried: 我需要对名称为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)
                          };

The SQL query that is generated is: 生成的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]

For me this seems to be complex. 对我来说,这似乎很复杂。 This can be achieved by following simple query: 可以通过以下简单查询来实现:

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

Is there a way in LINQ with which the SQL query that gets generated will have both the aggregation rather than having it two separate queries joined with nested queries? LINQ中是否有一种方法可以使生成的SQL查询既具有聚合功能,又可以使两个独立的查询与嵌套查询结合在一起?

From my experience with EF6, conditional Sum (ie Sum(condition ? 1 : 0) ) is translated much better to SQL than Count with predicate (ie Count(condition) ): 从我的经验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)
    };

Btw, your SQL example should be using SUM as well. 顺便说一句,您的SQL示例也应该使用SUM In order to utilize the SQL COUNT which excludes NULL s, it should be ELSE NULL or no ELSE : 为了利用排除NULL的SQL COUNT ,它应该是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

But there is no equivalent LINQ construct for this, hence no way to let EF6 generate such translation. 但是,对此没有等效的LINQ构造,因此没有办法让EF6生成这种转换。 But IMO the SUM is good enough equivalent. 但是IMO的SUM就足够了。

A much simpler query results from not using the unnecessary group by and just querying the table twice in the select : 一个简单得多的查询是由于不使用不必要的group by,而只是在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)
          };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM