【问题标题】:Linq - left join on multiple (OR) conditionsLinq - 在多个(OR)条件下左连接
【发布时间】:2009-08-12 08:49:01
【问题描述】:

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

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 分组,而不是像上面那样按 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

【问题讨论】:

  • 我建议你尝试我的查询没有分组,看看是否得到了你期望的结果。如果确实如此,则尝试找出分组失败的原因。也许按new { a.col1, a.col2 } 分组?
  • 作为基本的左连接选择工作正常 - 如果这是一个基本的等连接,则从分组中取出计数的行为并不像我预期的那样。请注意上面的额外信息。

标签: c# linq-to-sql


【解决方案1】:

LINQ 仅直接支持等值连接。如果你想做任何其他类型的连接,你基本上需要一个交叉连接和where

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

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

【讨论】:

  • 我已经开始使用这种方法......我实际上是在适当的查询中进行分组,以获取来自 a 的所有记录以及来自 b 的匹配记录的计数。我将编辑帖子以澄清。
【解决方案2】:

根据查询提供程序,您可以选择使用两个 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 子句拆分为两个单独的连接,然后采用它们的联合。 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)。

【讨论】:

  • 我正在使用 Linq to objects - 有替代解决方案吗?
猜你喜欢
  • 1970-01-01
  • 2011-01-10
  • 2012-01-08
  • 2012-02-12
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多