如何在LINQ中在单个联接中的多个字段上进行联接


244

我需要执行一个LINQ2DataSet查询,该查询对多个字段进行联接(如

var result = from x in entity
join y in entity2 
       on x.field1 = y.field1 
and 
          x.field2 = y.field2

我还找到了合适的解决方案(我可以在where子句中添加额外的约束,但这远非合适的解决方案,或者使用解决方案,但假定存在等价联接)。

在LINQ中是否可以在单个联接中联接多个字段?

编辑

var result = from x in entity
             join y in entity2
             on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

是我上面假设为均等连接的解决方案。

进一步编辑

为了回答批评我最初的例子是等值连接,我确实承认,我当前的要求是等值连接,并且我已经采用了上面提到的解决方案。

但是,我试图了解我在LINQ中应该/应该采用的可能性和最佳实践。我将需要尽快使用表ID进行日期范围查询联接,并且只是在先发制人,似乎我必须在where子句中添加日期范围。

与往常一样,感谢您提出的所有建议和意见


48
仅供参考,如果您在annon类中进行多字段联接,则必须将两个annon类中的字段命名为相同,否则会出现编译错误。
标记

6
或者,必须确保它们具有匹配的名称。即,您可以仅命名一种匿名类型的字段以使其与另一种匹配。
汤姆·弗格森


我在等号两边使用元组而不是对象,它似乎也起作用。
GHZ

Answers:


89

具有匿名类型的解决方案应该可以正常工作。LINQ 可以只表示等值连接(用联接子句,反正),而事实上,你已经什么的说你要根据你的原始查询反正表达。

如果由于某些特定原因而您不喜欢带有匿名类型的版本,则应说明该原因。

如果您想做的不是您最初要求的,请举一个您真正想做的事的例子。

编辑:响应问题中的编辑:是的,要执行“日期范围”联接,您需要使用where子句。它们实际上在语义上是等效的,因此这只是可用的优化的问题。Equijoins通过基于内部序列创建查找来提供简单的优化(在LINQ to Objects中,包括LINQ to DataSets)-将其视为从键到与键匹配的条目序列的哈希表。

使用日期范围进行此操作比较困难。但是,根据“日期范围联接”的确切含义,您可以执行类似的操作 -如果您计划创建日期的“范围”(例如每年一次),以便在日期范围内出现两个条目同一年(但不是同一日期)应该匹配,那么您只需使用该波段作为键即可。如果比较复杂,例如,连接的一侧提供了一个范围,而连接的另一侧提供了一个日期,如果它在该范围内,则匹配该日期,最好用一个where子句(第二个之后from条款)。您可以通过命令一侧或另一侧更有效地查找匹配项来做一些特别时髦的魔术,但这将需要大量工作-在检查性能是否是问题之后,我只会做这种事情。


谢谢,是的,性能是我使用where子句的主要担心。我猜想联接之后的where子句将对更大的数据集执行过滤,如果引入第二个join参数可以减少该数据集。我喜欢命令测试是否可以提高效率的想法
johnc

您将拥有几条记录?不要忘了,从头开始对结果进行排序将需要一定的时间才能开始...
Jon Skeet

“它们实际上在语义上是等效的”-我们是否需要在其中使用“真的”这个词?也许您的意思是,“它们在语义上确实是等效的” :)
某天

136
var result = from x in entity
   join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

这就是我一直在寻找的东西,因为101 Linq样本没有此功能,或者至少没有看到。
克里斯·马里西奇

1
@PeterX确实可以,请在此处查看我的答案:stackoverflow.com/a/22176658/595157
niieani 2014年

13
上面的代码不起作用。添加之后,on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 } 它起作用了
Ravi Ram

@Ravi Ram ..谢谢..您的评论有所帮助
NMathur

80
var result = from x in entity1
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }

如果两个实体中的列名不同,则需要执行此操作。


6
感谢您提及不同的列名。这解决了我不好的表情。
ʀʀʏ2015年

1
这也对我有用。如果列名不匹配,则会出现此错误,“ join子句中的表达式之一的类型不正确。对'GroupJoin'的调用中类型推断失败。”
2013年

感谢您为关键变量添加别名。
Thomas.Benz

当我没有将int的所有属性都命名为'new {}'时,我遇到了@humbads提到的错误。因此,只要您说出一个,就必须说出其他人的名字。
伊桑·梅拉梅

非常感谢
Charly H

51

只需使用等效的方法链语法来完成此操作:

entity.Join(entity2, x => new {x.Field1, x.Field2},
                     y => new {y.Field1, y.Field2}, (x, y) => x);

最后一个参数(x, y) => x是您选择的参数(在上述情况下,我们选择x)。


31

我认为一个更具可读性和灵活性的选择是使用Where函数:

var result = from x in entity1
             from y in entity2
                 .Where(y => y.field1 == x.field1 && y.field2 == x.field2)

通过附加.DefaultIfEmpty(),这还允许轻松地从内部联接更改为左联接。


现在作为lambda的长期用户(而不是我问问题时),我必须同意
johnc

这会更慢吗?
AlfredBr '16

1
我认为它应该具有与新{ ... } equals new { ... }语法相同的性能。LinqPad是一个很好的工具,可以查看表达式的行为方式(如果使用LINQ2SQL,则可以使用SQL脚本,表达式树等)
Alexei

据我所知,它是在生产CROSS JOIN而不是INNER JOIN
Mariusz

@Mariusz是的,生成CROSS JOIN + WHERE而不是INNER JOIN是有意义的。对于简单的查询,我希望分析器会生成非常相似的内容。
Alexei

10
var result = from x in entity
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
             select new 
             {
               /// Columns
              };

8

你可以做类似下面的事情

var query = from p in context.T1

        join q in context.T2

        on

        new { p.Col1, p.Col2 }

        equals

         new { q.Col1, q.Col2 }

        select new {p...., q......};

正如我刚才提到的那样,这需要同等的参与
johnc

7

使用联接运算符只能执行等联接。可以使用其他运算符构造其他类型的联接。我不确定使用这些方法还是通过更改where子句,您尝试执行的确切联接是否会更容易。有关join子句的文档可在此处找到。MSDN上也有一篇有关联接操作文章,文章还具有指向其他联接示例的多个链接。


3

如果实体中的字段名称不同

var result = from x in entity
   join y in entity2 on 
          new {
                field1=   x.field1,
               field2 =  x.field2 
             } 
          equals
         new { 
                field1= y.field1,
                field2=  y.myfield
              }
select new {x,y});

谢谢。名称匹配是我所缺少的。
布雷特(Brett),

2

作为完整的方法链,如下所示:

lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
                (a, b) => new ResultItem
                {
                    Id = a.Id,
                    ATotal = a.Total,
                    BTotal = b.Total
                }).ToList();

-2
from d in db.CourseDispatches
                             join du in db.DispatchUsers on d.id equals du.dispatch_id
                             join u in db.Users on du.user_id equals u.id
                             join fr in db.Forumreports on (d.course_id + '_' + du.user_id)  equals  (fr.course_id + '_'+ fr.uid)

这对我有用


这是针对多个
联接的

-3

声明一个Class(Type)来保存要加入的元素。在下面的示例中,声明JoinElement

 public class **JoinElement**
{
    public int? Id { get; set; }
    public string Name { get; set; }

}

results = from course in courseQueryable.AsQueryable()
                  join agency in agencyQueryable.AsQueryable()
                   on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper } 
                   equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1

1
这已经在9年前得到了答案……这个答案带来了什么价值?
Maciej Jureczko
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.