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

谢谢!

Answers:


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(fieldname)将计算该字段中不为空的行。也许我没有收到您的问题,请说明是否是这种情况。
Mehrdad Afshari,2009年

我想我一直在对行计数方面进行思考,但是您是正确的,仅对非空值进行计数。谢谢。
pbz

1
.Count()将生成COUNT(*),它将对该组中的所有行进行计数。
Mehrdad Afshari,2009年

我有完全相同的问题,但是比较t => t.ChildID!= null对我不起作用。结果始终是一个空对象,Resharper抱怨该表达式始终为真。所以我使用了(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 2010年

1
在原始问题和已答应的答案的上下文中,您的评论没有任何意义。另外-如果您想要的不仅仅是键,则可以从中提取整个父行。
艾米B 2010年

具有let关键字的解决方案将生成与@Mosh组加入的解决方案相同的子查询。
Mohsen Afshin

@MohsenAfshin是的,它会生成一个子查询,该子查询与上面直接在我的答案中带有子查询的查询相同。
Amy B

39

晚期答案:

不应该需要的左连接在所有如果你正在做的是伯爵()。请注意,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 join into等效于GroupJoin(而join不含intois 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。在这种情况下,我们不需要进行分组,因为join into是一个分组连接本身。

这是我的解决方案:

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

与这里投票最多的解决方案不同,在Count(t => t.ChildId!= null)中,我们不需要j1j2和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) })
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.