Linq – 左连接多个(OR)条件

我需要在条件为OR而不是AND s的多个条件下进行左连接。 我已经找到了很多后者的样本,但我很难为我的场景找到正确的答案。

 from a in tablea join b in tableb on new { a.col1, a.col2 } equals new { b.col1, b.col2 } group a by a into g select new () { col1 = a.col1, col2 = a.col2, count = g.Count() } 

适用于所有条件必须匹配的连接。 我需要让连接匹配on a.col1 = b.col1 OR a.col2 = b.col2

我知道这一定很容易但我在这个问题上一片空白!

编辑:

为了提供更多信息,查询的目的是获得包含“a”中所有字段的投影以及“b”中匹配记录的计数。 我修改了上面的示例,试图说明我追求的是什么。 当我使用上述方法运行时,Jon Skeet已经注意到我从a获得了所有记录的计数,而不是b中相关记录的计数。

基本的左连接工作正常:

 from a in tablea from b in tableb .Where( b => ( a.col1 == b.col1 || a.col2 == b.col2)) .DefaultIfEmpty() select new { col1 = a.col1, col2 = a.col2 } 

如果我修改它以添加如下分组

 from a in tablea from b in tableb .Where( b => ( a.col1 == b.col1 || a.col2 == b.col2)) .DefaultIfEmpty() group a by a.col1 into g select new { col1 = g.Key, count = g.Count() } 

我得到了从a返回的记录的计数 – 而不是b中匹配的记录数。

编辑:

我会给Jon一个答案 – 我已经解决了我的计数问题 – 我没有意识到我可以使用lamda来过滤计数(g.Count(x => x != null)) 。 另外,我需要按照上面的方式对b进行分组,而不是按照a进行分组。 这给出了正确的结果,但SQL不像我手工编写的那样高效,因为它添加了一个相关的子查询 – 如果有人可以建议更好的方式来编写它来模拟以下SQL我会很感激!

 select a.col1, count(b.col1) from tablea a left join tableb b on a.col1 = b.col1 or a.col2 = b.col2 group by a.col1 

LINQ只直接支持equijoins。 如果你想做任何其他类型的加入,你基本上需要一个交叉连接, where

 from a in tablea from b in tableb where a.col1 == b.col1 || a.col2 == b.col2 select ... 

可能值得检查生成的SQL是什么样的以及查询计划是什么样的。 可能有更有效的方法,但这可能是最简单的方法。

根据查询提供程序,您可以选择使用两个from子句:

 from a in tablea from b in tableb where a.col1 == b.col1 || a.col2 == b.col2 

如果您在数据库上执行,那将同样有效。 如果您执行内存(Linq to Objects),这将枚举所有可能的组合,这可能是低效的。

Arg,Skeeted ;-)。

更高效的Linq to Objects替代品是可能的。 join运算符只枚举每个源一次,然后执行散列连接,因此您可以将or-clause拆分为两个单独的连接,然后将它们联合起来。 linq中的联合只是一个没有重复的连接,所以看起来如下:

 (from a in tablea join b in tableb on a.Col1 equals b.Col1 select new {a, b}) .Concat( from a in tablea join b in tableb on a.Col2 equals b.Col2 select new {a, b} ).Distinct() 

这种方法很有效,而且它只是一个查询,但从某种意义上说,代码的性能特征依赖于详细了解linq的工作方式,这有点不明显。 就个人而言,如果你想进行可能多次匹配的散列连接,一个更明显的工具是ToLookup 。 使用它的替代方案可能如下所示:

 var bBy1 = tableb.ToLookup(b=>b.Col1); var bBy2 = tableb.ToLookup(b=>b.Col2); var q3 = from a in tablea from b in bBy1[a.Col1].Concat(bBy2[a.Col2]).Distinct() ... 

这个解决方案实际上更短,它工作的原因更明显,所以这是我更喜欢的。 请记住,如果你拆分|| 运算符分为两个独立的查询,如上述两种情况,您需要手动避免重复计算结果(即使用Distinct )。