LINQ - Gabung Kiri, Kelompokkan, dan Hitung

166

Katakanlah saya punya SQL ini:

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

Bagaimana saya bisa menerjemahkan ini ke dalam LINQ ke SQL? Saya terjebak di COUNT (c.ChildId), SQL yang dihasilkan sepertinya selalu menghasilkan COUNT (*). Inilah yang saya dapatkan sejauh ini:

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

Terima kasih!

pbz
sumber

Jawaban:

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) }
Mehrdad Afshari
sumber
OK, itu berhasil, tapi mengapa? Bagaimana Anda memikirkannya? Bagaimana tidak menghitung nilai nol memberi kita sama dengan COUNT (c.ChildId)? Terima kasih.
pbz
4
Inilah cara kerja SQL. COUNT (bidang nama) akan menghitung baris di bidang itu yang tidak nol. Mungkin saya tidak mendapatkan pertanyaan Anda, mohon klarifikasi jika itu masalahnya.
Mehrdad Afshari
Saya kira saya selalu memikirkannya dalam hal menghitung baris, tetapi Anda benar, hanya nilai-nilai non-nol yang dihitung. Terima kasih.
pbz
1
.Count () akan menghasilkan COUNT (*) yang akan menghitung semua baris dalam grup itu.
Mehrdad Afshari
Saya memiliki masalah yang sama namun membandingkan t => t.ChildID! = Null tidak bekerja untuk saya. Hasilnya selalu berupa objek nol dan Resharper mengeluh bahwa ungkapan itu selalu benar. Jadi saya menggunakan (t => t! = Null) dan itu berhasil untuk saya.
Joe
55

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

Jika tipe kueri dihubungkan oleh asosiasi, ini menyederhanakan untuk:

from p in context.ParentTable 
let cCount = p.Children.Count()
select new { ParentId = p.Key, Count = cCount } ;
Amy B
sumber
Jika saya ingat dengan benar (sudah lama), permintaan itu adalah versi sederhana dari yang besar. Jika yang saya butuhkan adalah kuncinya dan hitung solusi Anda akan lebih bersih / lebih baik.
pbz
1
Komentar Anda tidak masuk akal dalam konteks dengan pertanyaan asli dan jawaban terangkat. Selain itu - jika Anda ingin lebih dari kunci, Anda memiliki seluruh baris induk untuk menarik.
Amy B
Solusi dengan letkata kunci akan menghasilkan subquery yang sama dengan solusi gabungan @Mosh group.
Mohsen Afshin
@MohsenAfshin ya, itu menghasilkan subquery sama dengan permintaan dengan subquery dalam jawaban saya langsung di atasnya.
Amy B
39

JAWABAN TERLAMBAT:

Anda seharusnya tidak perlu bergabung dengan kiri sama sekali jika semua yang Anda lakukan adalah Hitung (). Perhatikan bahwa join...intosebenarnya diterjemahkan GroupJoinyang mengembalikan pengelompokan seperti new{parent,IEnumerable<child>}sehingga Anda hanya perlu memanggil Count()grup:

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

Dalam sintaksis Metode Ekstensi a join intosetara dengan GroupJoin(sementara jointanpa intoadalah Join):

context.ParentTable
    .GroupJoin(
                   inner: context.ChildTable
        outerKeySelector: parent => parent.ParentId,
        innerKeySelector: child => child.ParentId,
          resultSelector: (parent, children) => new { parent.Id, Count = children.Count() }
    );
Eren Ersönmez
sumber
8

Walaupun ide di balik sintaksis LINQ adalah untuk meniru sintaks SQL, Anda tidak harus selalu berpikir untuk secara langsung menerjemahkan kode SQL Anda ke dalam LINQ. Dalam kasus khusus ini, kita tidak perlu melakukan group into sejak bergabung adalah grup join sendiri.

Inilah solusi saya:

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

Tidak seperti solusi yang paling banyak dipilih di sini, kita tidak perlu memeriksa j1 , j2 , dan null dalam Hitungan (t => t.ChildId! = Null)

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

sumber