LINQ - ліві приєднання, групування за рахунком та підрахунок


166

Скажімо, у мене є цей SQL:

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

Як я можу перевести це в LINQ в SQL? Я застряг у COUNT (c.ChildId), згенерований SQL завжди видається COUNT (*). Ось що я отримав поки що:

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

Дякую!

Відповіді:


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

Гаразд, це працює, але чому? Як ви думаєте через це? Як підрахунок нульових значень не дає нам те саме, що і COUNT (c.ChildId)? Дякую.
pbz

4
Так працює SQL. COUNT (назва поля) буде рахувати рядки в цьому полі, які не є нульовими. Можливо, у мене не виникає запитання, уточнюйте, чи так це.
Мехрдад Афшарі

Я думаю, я завжди думав про це з точки зору підрахунку рядків, але ви правильні, рахуються лише ненульові значення. Дякую.
PBZ

1
.Count () генерує COUNT (*), який, до речі, буде рахувати всі рядки в цій групі.
Мехрдад Афшарі

У мене була точно така ж проблема, але порівняння t => t.ChildID! = Null не працювало для мене. Результат завжди був нульовим об'єктом, і Решарпер скаржився, що вираз завжди був вірним. Тому я використав (t => t! = Null) і це працювало на мене.
Джо

55

Подумайте про використання підзапросу:

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

Якщо типи запитів пов'язані асоціацією, це спрощує:

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

Якщо я правильно пам’ятаю (минув певний час), цей запит був спрощеною версією великого. Якби все, що мені було потрібно, було ключовим і порахувати, що ваше рішення було б чистішим / кращим.
pbz

1
Ваш коментар не має сенсу в контексті оригінального запитання та висококваліфікованих відповідей. Крім того - якщо ви хочете більше, ніж ключ, у вас є весь батьківський рядок.
Емі Б

Рішення з letключовим словом генерує підзапит, такий же, як і приєднане рішення до групи @Mosh.
Мохсен Афшин

@MohsenAfshin так, він генерує підзапит, такий самий, як запит із підзапитом у моїй відповіді безпосередньо над ним.
Емі Б

39

ПОСЛІДНИЙ ВІДПОВІДЬ:

Вам зовсім не потрібно мати ліве з'єднання, якщо все, що ви робите, це Count (). Зауважте, що join...intoнасправді перекладено, GroupJoinщо повертає групи, як-от new{parent,IEnumerable<child>}так вам просто потрібно зателефонувати Count()до групи:

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

У синтаксисі a Метод розширення a join intoеквівалентний GroupJoin(у той час як a joinбез 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

Хоча ідея синтаксису LINQ полягає в тому, щоб наслідувати синтаксис SQL, ви не завжди повинні думати про те, щоб безпосередньо перевести свій SQL-код у LINQ. У цьому конкретному випадку нам не потрібно робити групу, оскільки приєднання до групи - це само приєднання.

Ось моє рішення:

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

На відміну від рішення, яке головним чином проголосовано, нам не потрібно j1 , j2 та перевірки нуля в 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) })
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.