Linq左外连接计数

时间:2016-08-19 06:36:22

标签: c# sql-server linq

我想创建这个SQL查询:

SELECT 
    a.[Seat], 
    b.[PlayerId], 
    b.[UserName], 
    b.[NickName],
    COUNT(c.PlayerId) AS Trophy
    FROM   [dbo].[tbl_PlayerTableSeat] AS a
    INNER JOIN [dbo].[tbl_Player] AS b ON a.[PlayerId] = b.[PlayerId]               
    INNER JOIN [dbo].[tbl_GameVirtualTable] AS d ON d.GameVirtualTableId = a.GameVirtualTableId
    LEFT OUTER JOIN [dbo].[tbl_PlayerTableWinning] AS c ON a.[PlayerId] = c.[PlayerId] AND c.GameTableId = d.GameTableId                
    WHERE a.GameVirtualTableId = 36
    GROUP BY a.[Seat], b.[PlayerId], b.[UserName], b.[NickName]

我有这个Linq

var virtualTableSeatList = (from s in db.PlayerTableSeat
                        join p in db.Player on s.PlayerId equals p.PlayerId
                        join v in db.GameVirtualTable on s.GameVirtualTableId equals v.GameVirtualTableId
                        join w in db.PlayerTableWinning on new { X1 = s.PlayerId, X2 = v.GameTableId } equals new { X1 = w.PlayerId, X2 = w.GameTableId } into gj

                        from g in gj.DefaultIfEmpty()
                        where s.GameVirtualTableId == virtualGameTableId
                        group new { p, s } by new { p.PlayerId, s.Seat, p.NickName, p.UserName } into grp

                        select new VirtualTableSeatDto
                        {
                            PlayerId = grp.Key.PlayerId,
                            Seat = grp.Key.Seat,
                            NickName = grp.Key.NickName,
                            UserName = grp.Key.UserName,                                            
                            Trophy = grp.Count()
                        }
               ).ToList();

从SQL Profiler中,Linq生成此SQL查询:

exec sp_executesql N'SELECT 
[GroupBy1].[K2] AS [PlayerId], 
 CAST( [GroupBy1].[K1] AS int) AS [C1], 
[GroupBy1].[K4] AS [NickName], 
[GroupBy1].[K3] AS [UserName], 
[GroupBy1].[A1] AS [C2]
FROM ( SELECT 
    [Extent1].[Seat] AS [K1], 
    [Extent2].[PlayerId] AS [K2], 
    [Extent2].[UserName] AS [K3], 
    [Extent2].[NickName] AS [K4], 
    COUNT(1) AS [A1]
    FROM    [dbo].[tbl_PlayerTableSeat] AS [Extent1]
    INNER JOIN [dbo].[tbl_Player] AS [Extent2] ON [Extent1].[PlayerId] = [Extent2].[PlayerId]
    INNER JOIN [dbo].[tbl_GameVirtualTable] AS [Extent3] ON [Extent1].[GameVirtualTableId] = [Extent3].[GameVirtualTableId]
    LEFT OUTER JOIN [dbo].[tbl_PlayerTableWinning] AS [Extent4] ON ([Extent1].[PlayerId] = [Extent4].[PlayerId]) AND ([Extent3].[GameTableId] = [Extent4].[GameTableId])
    WHERE [Extent1].[GameVirtualTableId] = @p__linq__0
    GROUP BY [Extent1].[Seat], [Extent2].[PlayerId], [Extent2].[UserName], [Extent2].[NickName]
)  AS [GroupBy1]',N'@p__linq__0 int',@p__linq__0=36

我想将COUNT(1) AS [A1]更改为COUNT([Extent4].[PlayerId]) AS [A1]

所以它可以返回正确的数据。 我不知道如何更改LinQ

Trophy = grp.Count()

这样它可以计算PlayerId PlayerTableWinning而不是COUNT(1)

更新:@Ivan Stoev

将g添加到组中。

group new { p, s, g }

总结小组

Trophy = grp.Sum(item => item.w != null ? 1 : 0)

它会返回正确的答案。但是,它使用SUM而不是count。生成的SQL查询如下:

exec sp_executesql N'SELECT 
[GroupBy1].[K2] AS [PlayerId], 
 CAST( [GroupBy1].[K1] AS int) AS [C1], 
[GroupBy1].[K4] AS [NickName], 
[GroupBy1].[K3] AS [UserName], 
[GroupBy1].[A1] AS [C2]
FROM ( SELECT 
    [Filter1].[K1] AS [K1], 
    [Filter1].[K2] AS [K2], 
    [Filter1].[K3] AS [K3], 
    [Filter1].[K4] AS [K4], 
    SUM([Filter1].[A1]) AS [A1]
    FROM ( SELECT 
        [Extent1].[Seat] AS [K1], 
        [Extent2].[PlayerId] AS [K2], 
        [Extent2].[UserName] AS [K3], 
        [Extent2].[NickName] AS [K4], 
        CASE WHEN ( NOT (([Extent4].[GameTableId] IS NULL) AND ([Extent4].[PlayerId] IS NULL) AND ([Extent4].[GameRoundId] IS NULL))) THEN 1 ELSE 0 END AS [A1]
        FROM    [dbo].[tbl_PlayerTableSeat] AS [Extent1]
        INNER JOIN [dbo].[tbl_Player] AS [Extent2] ON [Extent1].[PlayerId] = [Extent2].[PlayerId]
        INNER JOIN [dbo].[tbl_GameVirtualTable] AS [Extent3] ON [Extent1].[GameVirtualTableId] = [Extent3].[GameVirtualTableId]
        LEFT OUTER JOIN [dbo].[tbl_PlayerTableWinning] AS [Extent4] ON ([Extent1].[PlayerId] = [Extent4].[PlayerId]) AND ([Extent3].[GameTableId] = [Extent4].[GameTableId])
        WHERE [Extent1].[GameVirtualTableId] = @p__linq__0
    )  AS [Filter1]
    GROUP BY [K1], [K2], [K3], [K4]
)  AS [GroupBy1]',N'@p__linq__0 int',@p__linq__0=36

1 个答案:

答案 0 :(得分:1)

SQL COUNT(field)COUNT(1)之间唯一(但重要的)区别是前者排除NULL值,当应用于右侧的正常必填字段时如果没有匹配的记录,左边的外连接就像你的情况一样会产生不同的结果 - 前者返回0而后者返回1.

“自然”LINQ等价物将是Count(field != null),但不幸的是,当前的EF查询提供程序将其转换为完全不同的SQL。所以在这种情况下,我个人使用更接近的等价表达式Sum(field != null ? 1 : 0),它可以产生更好的SQL。

要将上述内容应用于您的查询,您需要在分组内访问w,因此请更改

group new { p, s }

group new { p, s, w }

然后使用

Trophy = grp.Sum(item => item.w != null ? 1 : 0)