Linq Statement中的'let'生成交叉连接

时间:2015-11-18 06:49:19

标签: 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语句的探查器,它会显示交叉连接以获取每条记录的计数。

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语句的交叉连接开销,并且当记录数量巨大时可能会导致性能问题。

作为一个解决方案,我可以将data.Count()移到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

任何人都可以有比这更好的解决方案。或者,任何人都可以建议最好的方法让让我们进入linq或之前获得它?

2 个答案:

答案 0 :(得分:2)

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

如果您不相信将执行计划与您的替代计划进行比较。我只能想到使用子选择,但它对我来说并不好看。

子选择

SELECT 
    [UserId],
    (SELECT count(*) FROM [dbo].[Users]) as [Cnt]
FROM  [dbo].[Users]    
WHERE 1 = [IsActive]

答案 1 :(得分:2)

  

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

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

更新:为以下查询添加实际执行计划(请参阅how to)。您可以看到它正在加入标量值(例如,只运行一次Count选择查询)。

查询:

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]

执行计划:
enter image description here