2009-03-29 44 views
157

Hãy nói rằng tôi có SQL này:LINQ - Left Join, Nhóm By, và Đếm

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

Làm thế nào tôi có thể dịch này vào LINQ to SQL? Tôi đã bị mắc kẹt tại COUNT (c.ChildId), SQL được tạo luôn luôn có vẻ như đầu ra COUNT (*). Dưới đây là những gì tôi đã đạt được:

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

Cảm ơn bạn!

Trả lời

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

OK, điều đó có hiệu quả nhưng tại sao? Bạn nghĩ thế nào về nó? Làm thế nào để không tính giá trị null cho chúng ta giống như COUNT (c.ChildId)? Cảm ơn. – pbz

+4

Đây là cách SQL hoạt động. COUNT (tên trường) sẽ đếm các hàng trong trường đó không phải là rỗng. Có lẽ tôi không nhận được câu hỏi của bạn, xin vui lòng làm rõ nếu đó là trường hợp. –

+0

Tôi đoán tôi luôn luôn nghĩ về nó trong việc đếm các hàng, nhưng bạn là chính xác, chỉ có các giá trị không null được tính. Cảm ơn. – 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

Xem xét sử dụng một subquery:

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

Nếu các loại truy vấn được nối với nhau bằng một hiệp hội, điều này giúp đơn giản hoá để:

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

Nếu tôi nhớ chính xác (đã lâu rồi), truy vấn đó là một phiên bản đơn giản của bản lớn. Nếu tất cả những gì tôi cần là chìa khóa và tính giải pháp của bạn sẽ sạch hơn/tốt hơn. – pbz

+1

Nhận xét của bạn không có ý nghĩa trong ngữ cảnh với câu hỏi gốc và câu trả lời được bình chọn. Ngoài ra - nếu bạn muốn nhiều hơn khóa, bạn có toàn bộ hàng chính để vẽ. –

+0

điều này giúp tôi với một vấn đề liên quan phần nào, nhờ – Merritt

26

ĐÁP MUỘN:

Bạn không cần phải tham gia bên trái nếu tất cả những gì bạn đang làm là Count(). Lưu ý rằng join...into thực sự là dịch sang GroupJoin trả về nhóm như new{parent,IEnumerable<child>} vì vậy bạn chỉ cần gọi Count() trên nhóm:

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

Trong Phương pháp mở rộng cú pháp một join into tương đương với GroupJoin (trong khi một join mà không có một intoJoin):

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

Đó là một giải pháp tuyệt vời và đơn giản cho vấn đề này, cảm ơn bạn! –

+0

điều này tốt hơn các giải pháp khác và phải là câu trả lời cho câu hỏi này. –

5

Trong khi ý tưởng đằng sau cú pháp LINQ là mô phỏng cú pháp SQL, bạn không nên luôn nghĩ trực tiếp dịch mã SQL của bạn sang LINQ. Trong trường hợp cụ thể này, chúng tôi không cần phải làm nhóm thành kể từ tham gia vào là một nhóm tự tham gia.

Đây là giải pháp của tôi:

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

Không giống như hầu hết là bình chọn giải pháp ở đây, chúng ta không cần j1, j2 và kiểm tra null trong Count (t => t.ChildId = null!)