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

时间:2009-08-12 08:49:02

标签: c# linq-to-sql

我需要在条件为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分组,而不是按照上面的方式分组。这给出了正确的结果,但SQL不像我手工编写的那样有效,因为它添加了一个相关的子查询 - 如果有人可以建议更好的方式来编写它来模拟以下SQL我会很感激! / p>

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

2 个答案:

答案 0 :(得分:35)

LINQ只直接支持equijoins。如果您想进行任何其他类型的加入,您基本上需要交叉加入和where

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

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

答案 1 :(得分:21)

根据查询提供程序的不同,您可以选择使用两个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)。