LINQ: join sinistro, raggruppa per e conteggio


166

Diciamo che ho questo SQL:

SELECT p.ParentId, COUNT(c.ChildId)
FROM ParentTable p
  LEFT OUTER JOIN ChildTable c ON p.ParentId = c.ChildParentId
GROUP BY p.ParentId

Come posso tradurlo in LINQ in SQL? Mi sono bloccato su COUNT (c.ChildId), l'SQL generato sembra sempre generare COUNT (*). Ecco cosa ho ottenuto finora:

from p in context.ParentTable
join c in context.ChildTable on p.ParentId equals c.ChildParentId into j1
from j2 in j1.DefaultIfEmpty()
group j2 by p.ParentId into grouped
select new { ParentId = grouped.Key, Count = grouped.Count() }

Grazie!

Risposte:


189
from p in context.ParentTable
join c in context.ChildTable on p.ParentId equals c.ChildParentId into j1
from j2 in j1.DefaultIfEmpty()
group j2 by p.ParentId into grouped
select new { ParentId = grouped.Key, Count = grouped.Count(t=>t.ChildId != null) }

OK, funziona, ma perché? Come ci pensi? In che modo il conteggio di valori null ci dà lo stesso di COUNT (c.ChildId)? Grazie.
pbz,

4
Ecco come funziona SQL. COUNT (fieldname) conterà le righe in quel campo che non sono null. Forse non capisco la tua domanda, per favore chiarisci se è così.
Mehrdad Afshari,

Immagino di averci sempre pensato in termini di conteggio delle righe, ma hai ragione, vengono contati solo i valori non nulli. Grazie.
pbz,

1
.Count () genererà COUNT (*) che conterà tutte le righe di quel gruppo.
Mehrdad Afshari,

Ho avuto lo stesso identico problema, tuttavia confrontando t => t.ChildID! = Null non ha funzionato per me. Il risultato è sempre stato un oggetto nullo e Resharper si è lamentato del fatto che l'espressione era sempre vera. Quindi ho usato (t => t! = Null) e ha funzionato per me.
Joe

55

Prendi in considerazione l'utilizzo di una sottoquery:

from p in context.ParentTable 
let cCount =
(
  from c in context.ChildTable
  where p.ParentId == c.ChildParentId
  select c
).Count()
select new { ParentId = p.Key, Count = cCount } ;

Se i tipi di query sono collegati da un'associazione, questo semplifica:

from p in context.ParentTable 
let cCount = p.Children.Count()
select new { ParentId = p.Key, Count = cCount } ;

Se ricordo bene (è passato un po 'di tempo), quella query era una versione semplificata di una grande. Se tutto ciò di cui avevo bisogno fosse la chiave e conta la tua soluzione sarebbe stata più pulita / migliore.
pbz,

1
Il tuo commento non ha senso nel contesto della domanda originale e delle risposte votate. Inoltre, se desideri più della chiave, hai l'intera riga principale da cui attingere.
Amy B,

La soluzione con letparola chiave genererà una sottoquery uguale alla soluzione unita al gruppo @Mosh.
Mohsen Afshin,

@MohsenAfshin sì, genera una sottoquery uguale alla query con una sottoquery nella mia risposta direttamente sopra di essa.
Amy B,

39

RISPOSTA IN RITARDO:

Non dovresti aver bisogno del join sinistro se tutto ciò che stai facendo è Count (). Si noti che in join...intorealtà viene tradotto in GroupJoincui vengono restituiti raggruppamenti simili, new{parent,IEnumerable<child>}quindi è sufficiente chiamare Count()il gruppo:

from p in context.ParentTable
join c in context.ChildTable on p.ParentId equals c.ChildParentId into g
select new { ParentId = p.Id, Count = g.Count() }

Nella sintassi del metodo di estensione a join intoè equivalente a GroupJoin(mentre a joinsenza an intoè Join):

context.ParentTable
    .GroupJoin(
                   inner: context.ChildTable
        outerKeySelector: parent => parent.ParentId,
        innerKeySelector: child => child.ParentId,
          resultSelector: (parent, children) => new { parent.Id, Count = children.Count() }
    );

8

Mentre l'idea alla base della sintassi LINQ è emulare la sintassi SQL, non dovresti sempre pensare di tradurre direttamente il tuo codice SQL in LINQ. In questo caso particolare, non è necessario eseguire il raggruppamento in poiché join into è un join di gruppo stesso.

Ecco la mia soluzione:

from p in context.ParentTable
join c in context.ChildTable on p.ParentId equals c.ChildParentId into joined
select new { ParentId = p.ParentId, Count = joined.Count() }

A differenza della soluzione più votata qui, non abbiamo bisogno di j1 , j2 e controllo null in Count (t => t.ChildId! = Null)


7
 (from p in context.ParentTable     
  join c in context.ChildTable 
    on p.ParentId equals c.ChildParentId into j1 
  from j2 in j1.DefaultIfEmpty() 
     select new { 
          ParentId = p.ParentId,
         ChildId = j2==null? 0 : 1 
      })
   .GroupBy(o=>o.ParentId) 
   .Select(o=>new { ParentId = o.key, Count = o.Sum(p=>p.ChildId) })
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.