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.