170 votes

LINQ - Jointure gauche, Grouper par et Compter

Disons que j'ai cette instruction SQL :

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

Comment puis-je traduire cela en LINQ to SQL ? Je suis bloqué sur le COUNT(c.ChildId), le SQL généré semble toujours produire COUNT(*). Voici ce que j'ai jusqu'à présent :

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

Merci !

194voto

Mehrdad Afshari Points 204872
de p in context.ParentTable
joindre c dans le contexte.ChildTable on p.ParentId équivaut à c.ChildParentId dans j1 
de j2 dans j1.DefaultIfEmpty()
regrouper j2 par p.ParentId dans regroupé
sélectionner new { ParentId = regroupé.Key, Count = regroupé.Count(t=>t.ChildId != null) }

0 votes

D'accord, ça fonctionne, mais pourquoi ? Comment y réfléchissez-vous ? En quoi le fait de ne pas compter les valeurs nulles nous donne-t-il la même chose que COUNT(c.ChildId) ? Merci.

4 votes

Voici comment fonctionne SQL. COUNT(nomchamp) comptera les lignes de ce champ qui ne sont pas nulles. Peut-être que je ne comprends pas votre question, veuillez clarifier si c'est le cas.

0 votes

Je suppose que j'ai toujours pensé en termes de comptage de lignes, mais vous avez raison, seules les valeurs non nulles sont comptées. Merci.

59voto

David B Points 53123

Considérez l'utilisation d'une sous-requête :

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 } ;

Si les types de requête sont connectés par une association, cela se simplifie à :

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

0 votes

Si je me souviens bien (ça fait un moment), cette requête était une version simplifiée d'une plus grande. Si tout ce dont j'avais besoin était la clé et le compte, votre solution aurait été plus propre / meilleure.

1 votes

Votre commentaire n'a pas de sens dans le contexte de la question d'origine et des réponses votées. De plus, si vous voulez plus que la clé, vous avez toute la ligne parente à partir de laquelle vous pouvez vous inspirer.

0 votes

La solution avec le mot-clé let générera une sous-requête comme la solution de jointure de groupe @Mosh.

39voto

Eren Ersönmez Points 13491

RÉPONSE TARDIVE:

Vous ne devriez pas avoir besoin de la jointure gauche du tout si tout ce que vous faites est Count(). Notez que join...into est en fait traduit en GroupJoin qui renvoie des regroupements comme new{parent,IEnumerable} donc vous devez simplement appeler Count() sur le groupe:

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

Dans la syntaxe de méthode d'extension, un join into est équivalent à GroupJoin (tandis qu'un join sans un into est Join):

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

8voto

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

8voto

Mosh Points 1436

Alors que l'idée derrière la syntaxe LINQ vise à émuler la syntaxe SQL, vous ne devriez pas toujours penser à traduire directement votre code SQL en LINQ. Dans ce cas particulier, nous n'avons pas besoin de faire un group into puisque join into est en soi un group join.

Voici ma solution:

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

Contrairement à la solution la plus votée ici, nous n'avons pas besoin de j1, j2 et de vérification de null dans Count(t => t.ChildId != null)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X