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

ありがとうございました!

回答:


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

OK、それでうまくいきますが、なぜですか?どう思いますか?null値をカウントしないと、COUNT(c.ChildId)と同じ結果になりますか?ありがとう。
pbz 2009年

4
これがSQLの仕組みです。COUNT(fieldname)は、そのフィールドのnullでない行をカウントします。多分私はあなたの質問を得ない、それが事実であるかどうか明確にしてください。
Mehrdad Afshari、

行のカウントに関して常に考えていたと思いますが、正解です。null以外の値のみがカウントされます。ありがとう。
pbz 2009年

1
.Count()は、そのグループのすべての行をカウントするCOUNT(*)を生成します。
Mehrdad Afshari、

まったく同じ問題がありましたが、t => t.ChildID!= nullを比較してもうまくいきませんでした。結果は常に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 2010

1
あなたのコメントは、元の質問や賛成投票の回答との関連では意味がありません。さらに、キー以上のものが必要な場合は、親行全体を描画できます。
エイミーB

letキーワード付きのソリューションは、@ Moshグループ結合ソリューションと同じようにサブクエリを生成します。
Mohsenアフシン

@MohsenAfshinはい、それは私の答えの真上にサブクエリを持つクエリと同じサブクエリを生成します。
エイミー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は、a はGroupJoin(is joinなしの場合intoJoin)と同等です。

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はそれ自体がグループ結合であるため、group 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) })
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.