C# Linq语句中的“let”生成交叉联接

C# Linq语句中的“let”生成交叉联接,c#,sql-server,entity-framework,linq,C#,Sql Server,Entity Framework,Linq,考虑以下linq声明 var users = from a in dbContext.Users select a; var list = (from a in users let count = users.Count() where a.IsActive == true select new { a.UserId, count }).ToList(); 如果我们检查这个linq语句的

考虑以下linq声明

var users = from a in dbContext.Users
              select a;

var list = (from a in users
             let count = users.Count()
             where a.IsActive == true
             select new { a.UserId, count }).ToList();
如果我们检查这个linq语句的探查器,它会显示交叉连接以获取每个记录的计数

SELECT 
    [Extent1].[UserId] AS [UserId], 
    [GroupBy1].[A1] AS [C1]
    FROM  [dbo].[Users] AS [Extent1]
    CROSS JOIN  (SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Users] AS [Extent2] ) AS [GroupBy1]
    WHERE 1 = [Extent1].[IsActive]
我认为sql语句的交叉连接开销可能会导致记录数量巨大时的性能问题

作为一种解决方案,我可以移动这些数据。在linq语句之外进行计数,然后放入select,但这会导致两db的操作

var count = (from a in dbContext.Users
                            select a).Count();

var list = (from a in dbContext.Users
                            where a.IsActive == true
                            select new { a.UserId, count }).ToList();
SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Users] AS [Extent1]
    )  AS [GroupBy1]

exec sp_executesql N'SELECT 
    [Extent1].[UserId] AS [UserId], 
    @p__linq__0 AS [C1]
    FROM [dbo].[Users] AS [Extent1]
    WHERE 1 = [Extent1].[IsActive]',N'@p__linq__0 int',@p__linq__0=26
通过查看分析器,它将生成以下两个操作

var count = (from a in dbContext.Users
                            select a).Count();

var list = (from a in dbContext.Users
                            where a.IsActive == true
                            select new { a.UserId, count }).ToList();
SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Users] AS [Extent1]
    )  AS [GroupBy1]

exec sp_executesql N'SELECT 
    [Extent1].[UserId] AS [UserId], 
    @p__linq__0 AS [C1]
    FROM [dbo].[Users] AS [Extent1]
    WHERE 1 = [Extent1].[IsActive]',N'@p__linq__0 int',@p__linq__0=26

有谁能有比这更好的解决办法吗。或者,有谁能建议将let放入linq或之前获取它的最佳方法吗?

生成的sql不应该有任何性能问题。交叉连接会产生一条记录,无论表中有多少活动用户,优化器只需计算一次

如果您不确信,请将执行计划与您的备选方案进行比较。我只能考虑使用子选择,但在我看来并不是更好

子选择

我认为sql语句的交叉连接开销可能会导致记录数量巨大时的性能问题

不一定。请注意,这是连接到一个子查询,该子查询是数据计数的单行/列。您可以用不同的方式编写此查询,但最终,它需要加入以返回{UserId,count}。如果没有联接,则无法返回该数据。它现在所做的连接非常有效。因此,我建议不要尝试优化您没有的问题,即过早优化

更新:添加实际执行计划请参见以下查询。您可以看到它连接到一个标量值,例如只运行一次Count select查询

查询:

SELECT 
[Extent1].[UserId] AS [UserId], 
[GroupBy1].[A1] AS [C1]
FROM  [dbo].[Users] AS [Extent1]
CROSS JOIN  (SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Users] AS [Extent2] ) AS [GroupBy1]
WHERE 1 = [Extent1].[IsActive]
执行计划:

数据是如何声明的?您使用的是哪个版本的EF?我使用的是EF 6和.Net Framework 4.5。@JenishRabadiya:是的。它还生成交叉联接。同意,但您不认为下面的语句在联接中生成了同样多的select语句吗。i、 将执行select语句10倍以上的E10条记录。交叉连接将COUNT1从[dbo]选择为[A1]。[Users]选择为[Extent2]选择为[GroupBy1]@HarshadVekariya不,不应该。更新了答案以向您展示示例执行计划。请注意,它计算一个标量值计数,并将另一个查询与该标量值连接起来,只运行一次计数查询。感谢您发布执行计划,但如果您检查它,它将占用总执行量的近50%。@HarshadVekariya这是一件好事。它必须扫描两次:一次用于计数,另一次用于另一个查询。如果它必须对每一个用户都进行扫描,那么它将超过50%。