2016-08-19 6 views
1

ich diese SQL-Abfrage erstellen möchten:Linq Left Outer Join mit dem Grafen

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] 

ich diese Linq haben

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(); 

von SQL Profiler, die Linq diese SQL-Abfrage generiert:

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 

Ich möchte COUNT(1) AS [A1] zu COUNT([Extent4].[PlayerId]) AS [A1]

ändern

so kann es korrekte Daten zurückgeben. Ich habe keine Ahnung, wie die LinQ zu ändern

Trophy = grp.Count()

so dass es PlayerId von PlayerTableWinning statt COUNT zählen (1)


Aktualisiert: @Ivan Stoev

von Hinzufügen des g in die Gruppe.

group new { p, s, g } 

und die Summe die Gruppe

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

Es gibt die richtige Antwort. Es wird jedoch SUM statt Zählen verwendet. Die SQL-Abfrage erzeugt wird, wie unten:

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 
+0

Ist es nicht eigentlich das Gleiche? Auch in Ihrem sql - Sie zählen die 'playerId', aber eigentlich zählt nur die Anzahl der Datensätze in der Gruppe - es spielt also keine Rolle, ob es 1 ist oder die Spieler ID –

+0

es ist nicht unterschiedlich zwischen count (1) und count (Spieler-ID). was willst du eigentlich? –

+0

@EpoWilliam - Es sei denn, Sie möchten die genaue Menge von playerId wissen - ist das der Fall? –

Antwort

1

Der einzige (aber signifikant) Unterschied zwischen SQL COUNT(field) und COUNT(1) besteht darin, dass die erstere mit Ausnahme die NULL Werte, die an das normalerweise erforderlich Feld angelegt wird, wenn von der rechten Seite von Ein linker äußerer Join wie in Ihrem Fall erzeugt ein anderes Ergebnis, wenn es keine übereinstimmenden Datensätze gibt - der erste gibt 0 zurück, während der letzte 1 zurückgibt.

Das "natürliche" LINQ-Äquivalent wäre Count(field != null), aber das wird leider in ein übersetzt recht unterschiedliche SQL vom aktuellen EF-Query-Provider. In solchen Fällen benutze ich persönlich den näherliegenden äquivalenten Ausdruck Sum(field != null ? 1 : 0), der ein viel besseres SQL erzeugt.

Um das oben auf Ihre Anfrage anzuwenden, werden Sie einen Zugang zu w innerhalb der Gruppierung müssen, so ändern

group new { p, s } 

zu

group new { p, s, w } 

und verwenden Sie dann

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

Danke. Es gibt die richtige Antwort zurück. Es wird jedoch SUM anstelle von COUNT verwendet, da die SQL-Abfrage wie in der aktualisierten Frage angezeigt generiert wird. – EpoWilliam

+0

Ja, ich weiß (und ich denke, ich erwähnte warum in der Antwort). Dies ist der nächste Wert, den Sie von EF erhalten können. Es gibt kein LINQ-Konstrukt, das EF das exakte SQL-Konstrukt in der Frage generieren lässt. –

+0

Btw, 'item.w! = Null' funktioniert normalerweise gut, wenn Sie sich einem Feld anschließen. In Ihrem Fall erzeugt es redundante Überprüfungen, "(int?) Item.w.PlayerId! = Null" könnte eine bessere Wahl sein. –