2009-08-12 20 views
22

我需要在条件为OR s而不是AND s的多个条件下执行左连接。我发现了很多后者的样本,但我正努力为我的场景获得正确的答案。Linq - 在多个(或)条件下的左连接

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

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

编辑:

我给的答案乔恩 - 我已经解决了我的问题,计数 - 我没有意识到我可以用一个LAMDA过滤计数(g.Count(x => x != null))。另外,我需要按照上面的方式将b组合为a而不是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 
+0

我建议你尝试我的查询*没有分组,看看是否得到你期望的结果。如果确实如此,那么试着找出为什么分组失败。也许按'new {a.col1,a.col2}'分组? – 2009-08-12 09:06:12

+0

作为一个基本的左连接选择正常工作 - 如果这是一个基本的等连接,从组中获得计数的行为并不像我预期的那样。请注意上面的额外信息。 – 2009-08-12 09:25:57

回答

32

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

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

这也可能是值得检查一下生成的SQL看起来像什么的查询计划等。可能会有更有效的方法,但这可能是最简单的方法。

+0

我已经开始使用这种方法......我实际上将查询合并为从a获取所有记录并从b获取匹配记录的计数。我将编辑帖子以澄清。 – 2009-08-12 08:56:06

20

根据查询提供者,你可以只选择使用两个FROM子句:

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

其中,如果您对DB执行,将是多么有效。如果你在内存中执行(Linq to Objects),这将枚举所有可能的组合,这可能是低效的。

Arg,Skeeted ;-)。

更有效的Linq to Objects替代品是可能的。 join运算符只枚举一次每个源,然后进行散列连接,因此您可以将or子句拆分为两个独立的连接,然后进行联合。在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)。

+0

我正在使用Linq的对象 - 有没有其他解决方案? – Kev 2012-09-27 09:00:05

+0

当然,请参阅编辑! – 2012-09-27 12:57:36

+0

小心加入我? :http://chat.stackoverflow.com/rooms/info/17239/kryptonite – Kev 2012-09-27 13:16:21