如何在 LINQ 中对单个连接中的多个字段进行连接
Posted
技术标签:
【中文标题】如何在 LINQ 中对单个连接中的多个字段进行连接【英文标题】:How to do joins in LINQ on multiple fields in single join 【发布时间】:2010-09-27 06:37:47 【问题描述】:我需要执行一个 LINQ2DataSet 查询,该查询对多个字段进行连接(如
var result = from x in entity
join y in entity2
on x.field1 = y.field1
and
x.field2 = y.field2
我还没有找到合适的解决方案(我可以在 where 子句中添加额外的约束,但这远非合适的解决方案,或者使用 this 解决方案,但假设是等值连接)。
在 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 子句中添加日期范围。
一如既往地感谢您提供的所有建议和cmets
【问题讨论】:
仅供阅读本文的任何人参考,如果您在 annon 类中执行多字段连接,则必须将两个 annon 类中的字段命名为相同,否则会出现编译错误。 或者更确切地说,您必须确保它们具有匹配的名称。也就是说,您可以只命名其中一种匿名类型的字段,以使它们与另一种匹配。 关注这个答案***.com/a/34176502/1704458 我在等号的两侧使用元组而不是对象,它似乎也可以工作。 【参考方案1】:var result = from x in entity
join y in entity2 on new x.field1, x.field2 equals new y.field1, y.field2
【讨论】:
这是我想要看到的,因为 101 个 Linq 样本没有这个,或者至少我没有看到。 @PeterX 确实可以,在这里查看我的答案:***.com/a/22176658/595157 上面的代码不起作用。添加on new X1= x.field1, X2= x.field2 equals new X1=y.field1, X2= y.field2
后它工作了
@Ravi Ram .. 谢谢 .. 你的评论有帮助
还要确保数据类型匹配。 X1
的数据类型应该在两边相同。在我的情况下,第一个表中X1
的数据类型是Nullable Integer
,而在连接表中是Non-nullable integer
。然后我不得不将 Nullable 转换为不可为空的 Integer 以使其工作【参考方案2】:
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
如果两个实体中的列名不同,则需要这样做。
【讨论】:
感谢您提及不同的列名。这解决了我不好的表情。 这对我也有用。如果列名不匹配,您将收到此错误,“连接子句中的表达式之一的类型不正确。类型推断在调用 'GroupJoin' 时失败。” 感谢您为关键变量设置别名。 当我没有将 int 的所有属性命名为“new ”时,我收到了@humbads 提到的错误。所以仅供参考,如果你说出一个名字,你还必须说出其余的名字。 就我而言,我想使用OR
而不是AND
!我该怎么办? new ... equals new ....
会工作吗?【参考方案3】:
匿名类型的解决方案应该可以正常工作。 LINQ 可以只表示等值连接(无论如何都带有连接子句),实际上这就是你所说的你想要根据你的原始查询表达的内容。
如果您出于某种特定原因不喜欢匿名类型的版本,则应说明原因。
如果您想做的不是您最初要求的事情,请举例说明您真正想做的事情。
编辑:回应问题中的编辑:是的,要进行“日期范围”连接,您需要改用 where 子句。它们在语义上实际上是等价的,所以这只是可用优化的问题。 Equijoins 通过创建基于内部序列的查找来提供简单的优化(在 LINQ to Objects 中,其中包括 LINQ to DataSets) - 将其视为从键到与该键匹配的条目序列的哈希表。
使用日期范围执行此操作有些困难。但是,根据您所说的“日期范围加入”的确切含义,您可能能够做一些类似的事情 - 如果您计划创建日期的“带”(例如每年一个),例如同一年(但不在同一日期)出现的两个条目应该匹配,那么您只需使用该带作为键即可。如果它更复杂,例如连接的一侧提供一个范围,连接的另一侧提供一个日期,如果它在该范围内匹配,则最好使用where
子句(在第二个from
子句之后)IMO 处理.您可以通过订购一侧或另一侧来更有效地找到匹配项来做一些特别时髦的魔术,但这将是很多工作 - 我只会在检查性能是否存在问题后才会这样做。
【讨论】:
谢谢,是的,性能是我使用 where 子句的主要担心。我猜想连接之后的 where 子句将对更大的数据集执行过滤器,这可以通过引入第二个连接参数来减少。我确实喜欢订购以测试我是否可以获得效率提升的想法 您将拥有多少条记录?不要忘记排序结果开始需要一定的时间...... “它们在语义上真的是等价的”——我们需要“真的”这个词吗?也许您的意思是,“它们真的在语义上是等价的”:) @onedaywhen "它们在语义上真的是等价的" | “它们在语义上真的是等价的”具有讽刺意味的是:p【参考方案4】:只是用等效的方法链语法来完成这个:
entity.Join(entity2, x => new x.Field1, x.Field2,
y => new y.Field1, y.Field2, (x, y) => x);
虽然最后一个参数(x, y) => x
是您选择的(在上述情况下,我们选择x
)。
【讨论】:
生成的sql是什么样子的【参考方案5】:我认为一个更具可读性和灵活性的选择是使用 Where 函数:
var result = from x in entity1
from y in entity2
.Where(y => y.field1 == x.field1 && y.field2 == x.field2)
这还允许通过附加 .DefaultIfEmpty() 轻松地从内连接更改为左连接。
【讨论】:
作为一个长期的 lambda 用户(与我提出问题时相反),我不得不同意 会不会慢一些? 我认为它应该具有与新的 ... equals new ...
语法相同的性能。 LinqPad 是查看表达式行为方式的绝佳工具(如果使用 LINQ2SQL,则为 SQL 脚本、表达式树等)
据我所知,它产生的是 CROSS JOIN 而不是 INNER JOIN
@Mariusz 是的,生成 CROSS JOIN + WHERE 而不是 INNER JOIN 是有意义的。对于简单的查询,我希望分析器生成一个非常相似的结果。【参考方案6】:
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
;
【讨论】:
【参考方案7】:你可以做类似(如下)的事情
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......;
【讨论】:
正如我在问题中提到的,这需要等值连接【参考方案8】:使用连接运算符,您只能执行等值连接。可以使用其他运算符构造其他类型的连接。我不确定您尝试使用这些方法或更改 where 子句是否更容易进行确切的连接。可以在 here 找到有关 join 子句的文档。 MSDN 有一个article on join operations,其中还有多个指向其他连接示例的链接。
【讨论】:
【参考方案9】:如果实体中的字段名称不同
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);
【讨论】:
谢谢。名称匹配是我缺少的部分。【参考方案10】:完整的方法链如下所示:
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();
【讨论】:
【参考方案11】:我使用元组来做到这一点,这是两列的示例:
var list= list1.Join(list2,
e1 => (e1.val1,e1.val2),
e2 => (e2.val1,e2.val2),
(e1, e2) => e1).ToList();
【讨论】:
【参考方案12】: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)
这对我有用
【讨论】:
这个是多重连接,他想在一个连接中进行多个字段的连接【参考方案13】:声明一个 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
【讨论】:
这个9年前就有答案了……这个答案有什么价值?以上是关于如何在 LINQ 中对单个连接中的多个字段进行连接的主要内容,如果未能解决你的问题,请参考以下文章