2009-03-29 5 views
157

Допустим, у меня есть это SQL:LINQ - Left Join, Group By, и граф

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

Спасибо!

ответ

179
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) } 
+0

ОК, это работает, но почему? Как вы думаете, через это? Как не считать нулевые значения дают нам то же самое, что и COUNT (c.ChildId)? Благодарю. – pbz

+4

Так работает SQL. COUNT (имя поля) будет считать строки в этом поле, которые не равны нулю. Может быть, я не получу ваш вопрос, пожалуйста, уточните, так ли это. –

+0

Думаю, я всегда думал об этом с точки зрения подсчета строк, но вы правы, подсчитываются только ненулевые значения. Благодарю. – pbz

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) }) 
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 } ; 
+0

Если я правильно помню (прошло какое-то время), этот запрос был упрощенной версией большой. Если бы все, что мне было нужно, было ключевым и посчитало, что ваше решение было бы чище/лучше. – pbz

+1

Ваш комментарий не имеет смысла в контексте оригинального вопроса и ответов. Кроме того, если вы хотите больше, чем ключ, у вас есть целая родительская строка для рисования. –

+0

это помогает мне с некоторой проблемой, спасибо – Merritt

26

КОНЦЕ ОТВЕТ:

Вам Вам не нужно левое соединение, если все, что вы делаете, это 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() } 

В метод расширения синтаксиса join into эквивалентно GroupJoin (в то время как join без 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() } 
    ); 
+1

Это прекрасное и простое решение проблемы, спасибо! –

+0

это лучше, чем другие решения, и должен быть ответом на этот вопрос. –

5

Хотя идея синтаксиса 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 и нулевую проверку в графа (т => t.ChildId = нуль!)