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

ตกลงนั่นใช้ได้ แต่ทำไม คุณคิดยังไงกับมัน? การนับค่า null ไม่ให้เราเหมือนกับ COUNT (c.ChildId) อย่างไร ขอบคุณ
pbz

4
นี่คือการทำงานของ SQL COUNT (fieldname) จะนับแถวในฟิลด์นั้นที่ไม่เป็นโมฆะ บางทีฉันอาจไม่ได้รับคำถามของคุณโปรดอธิบายหากเป็นกรณีนี้
Mehrdad Afshari

ฉันคิดว่าฉันคิดถึงมันเสมอในแง่ของการนับแถว แต่คุณถูกต้องเฉพาะค่าที่ไม่เป็นโมฆะเท่านั้นที่จะถูกนับ ขอบคุณ
pbz

1
.Count () จะสร้าง COUNT (*) ซึ่งจะนับแถวทั้งหมดในกลุ่มนั้นตามวิธี
Mehrdad Afshari

ฉันมีปัญหาเดียวกันแน่นอน แต่การเปรียบเทียบ t => t.ChildID! = null ไม่ได้ผลสำหรับฉัน ผลที่ได้คือวัตถุที่เป็นโมฆะเสมอและ Resharper บ่นว่าการแสดงออกที่เป็นจริงเสมอ ดังนั้นฉันจึงใช้ (t => t! = null) และนั่นก็ใช้ได้สำหรับฉัน
Joe

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
ความคิดเห็นของคุณไม่สมเหตุสมผลในบริบทของคำถามต้นฉบับและคำตอบที่ยกระดับขึ้น นอกจากนี้ - หากคุณต้องการมากกว่าคีย์คุณจะต้องมีแถวพาเรนต์ทั้งหมด
Amy B

โซลูชันที่มีletคำหลักจะสร้างแบบสอบถามย่อยเหมือนกับกลุ่ม @Mosh ที่เข้าร่วมการแก้ปัญหา
Mohsen Afshin

@MohsenAfshin ใช่มันจะสร้างแบบสอบถามย่อยเหมือนกับแบบสอบถามที่มีแบบสอบถามย่อยในคำตอบของฉันโดยตรงเหนือมัน
Amy B

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

ในการขยายวิธีไวยากรณ์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() }
    );

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และ null ใน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) })
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.