LINQ to SQL:多个列上的多个连接。这是可能的吗?

154

给定:

一张名为 TABLE_1 的表,具有以下列:

  • ID
  • ColumnA
  • ColumnB
  • ColumnC

我有一个 SQL 查询,在该查询中,TABLE_1 根据 ColumnAColumnBColumnC 进行两次自连接。查询可能看起来像这样:

Select t1.ID, t2.ID, t3.ID
  From TABLE_1 t1
  Left Join TABLE_1 t2 On
       t1.ColumnA = t2.ColumnA
   And t1.ColumnB = t2.ColumnB
   And t1.ColumnC = t2.ColumnC
  Left Join TABLE_1 t3 On
       t2.ColumnA = t3.ColumnA
   And t2.ColumnB = t3.ColumnB
   And t2.ColumnC = t3.ColumnC
... and query continues on etc.

问题:

我需要将这个查询重写成LINQ语句。我已经尝试了以下代码:

var query =
    from t1 in myTABLE1List // List<TABLE_1>
    join t2 in myTABLE1List
      on t1.ColumnA equals t2.ColumnA
      && t1.ColumnB equals t2.ColumnA
    // ... and at this point intellisense is making it very obvious
    // I am doing something wrong :(

我该如何在LINQ中编写查询?我做错了什么吗?

8个回答

287

Linq to SQL中使用多个列进行连接有所不同。

var query =
    from t1 in myTABLE1List // List<TABLE_1>
    join t2 in myTABLE1List
      on new { t1.ColumnA, t1.ColumnB } equals new { t2.ColumnA, t2.ColumnB }
    ...

你需要利用匿名类型并组合一个类型,以便对多个列进行比较。

这似乎一开始很令人困惑,但一旦熟悉了SQL从表达式组成的方式,它就会变得更加清晰。在内部,这将生成你要查找的连接类型。

编辑 根据评论添加第二个连接的示例。

var query =
    from t1 in myTABLE1List // List<TABLE_1>
    join t2 in myTABLE1List
      on new { A = t1.ColumnA, B = t1.ColumnB } equals new { A = t2.ColumnA, B = t2.ColumnB }
    join t3 in myTABLE1List
      on new { A = t2.ColumnA, B =  t2.ColumnB } equals new { A = t3.ColumnA, B = t3.ColumnB }
    ...

4
这对于两个表连接很有效。我需要它适用于三个表的连接。抱歉,第二个代码块有些误导性。 - aarona
63
如果编译器出现了类型推断的错误,请检查两件事情:(1)类型是否相同,(2)列名是否相同。第二个问题是需要注意的地方。即使所有列都是字符类型,例如join T2 in db.tbl2 on new { T1.firstName, T1.secondName } equals new { T2.colFirst, T2.colSecond },这个示例也无法编译通过。但是,如果您将其更改为以下内容,则可以编译通过join T2 in db.tbl2 on new { N1 = T1.firstName, N2 = T1.secondName } equals new { N1 = T2.colFirst, N2 = T2.colSecond } - user2023861
4
命名问题可以通过以下方式消除: 从“myTABLE1List”中获取“t1”,再将其与“myTABLE1List”中的“t2”连接,使用新的匿名对象来比较“t1.ColumnA”和“t1.ColumnB”与“t2.ColumnA”和“t2.ColumnB”。 - Baqer Naqvi
1
请允许我编辑这个例子,因为它需要对匿名属性进行赋值。 - AceMark
1
这里有些问题...与LINQ有关。我可以在多个表上进行连接,也可以在多个字段上进行连接...但是,我不能同时进行两者,就像这个例子所示。因此,假设您只在1个字段上进行连接..并且您随后进行了第二次连接。如果您将第一次连接(或两者)更改为仅使用new {x.field} equals new {y.field},则会出现编译器错误。从功能上讲,您没有改变任何内容。使用.Net 4.6.1。 - user2415376
显示剩余2条评论

15

你也可以使用:

var query =
    from t1 in myTABLE1List 
    join t2 in myTABLE1List
      on new { ColA=t1.ColumnA, ColB=t1.ColumnB } equals new { ColA=t2.ColumnA, ColB=t2.ColumnB }
    join t3 in myTABLE1List
      on new {ColC=t2.ColumnA, ColD=t2.ColumnB } equals new { ColC=t3.ColumnA, ColD=t3.ColumnB }

6
啊!!这个方法有效!关键的不同之处在于你需要做 "ColA =" 部分,以便在其他联接中使用相同的字段。多年来我没有这样做,但也只需要在多个字段上进行一个联接。但现在我需要更多,只有像这个例子中给字段指定变量名称才能起作用。 - user2415376

13
在使用内连接时,LINQ2SQL很少需要显式进行连接。
如果您的数据库中有适当的外键关系,您将自动在LINQ设计器中获得一个关系(如果没有,则可以在设计器中手动创建一个关系,尽管您真的应该在数据库中拥有适当的关系)。

parent-child relation

然后您只需使用“点符号”访问相关表格

var q = from child in context.Childs
        where child.Parent.col2 == 4
        select new
        {
            childCol1 = child.col1,
            parentCol1 = child.Parent.col1,
        };

将生成查询

SELECT [t0].[col1] AS [childCol1], [t1].[col1] AS [parentCol1]
FROM [dbo].[Child] AS [t0]
INNER JOIN [dbo].[Parent] AS [t1] ON ([t1].[col1] = [t0].[col1]) AND ([t1].[col2] = [t0].[col2])
WHERE [t1].[col2] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [4]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1

在我看来,这种方法更易读,让你专注于特殊条件而不是连接的实际操作。
编辑: 当你想要与我们的数据库模型中的行进行连接时,这当然只适用于此。如果你想要在"模型之外"连接,则需要像Quintin Robinsonanswer一样手动连接。

12

Title_Authors是一个查找两个项目结果并继续链接的项目。

        DataClasses1DataContext db = new DataClasses1DataContext();
        var queryresults = from a in db.Authors                                          
                    join ba in db.Title_Authors                           
                    on a.Au_ID equals ba.Au_ID into idAuthor
                    from c in idAuthor
                    join t in db.Titles  
                    on c.ISBN equals t.ISBN 
                    select new { Author = a.Author1,Title= t.Title1 };

        foreach (var item in queryresults)
        {
            MessageBox.Show(item.Author);
            MessageBox.Show(item.Title);
            return;
        }

4

我想再举个例子,其中使用了多个(3个)连接。

 DataClasses1DataContext ctx = new DataClasses1DataContext();

        var Owners = ctx.OwnerMasters;
        var Category = ctx.CategoryMasters;
        var Status = ctx.StatusMasters;
        var Tasks = ctx.TaskMasters;

        var xyz = from t in Tasks
                  join c in Category
                  on t.TaskCategory equals c.CategoryID
                  join s in Status
                  on t.TaskStatus equals s.StatusID
                  join o in Owners
                  on t.TaskOwner equals o.OwnerID
                  select new
                  {
                      t.TaskID,
                      t.TaskShortDescription,
                      c.CategoryName,
                      s.StatusName,
                      o.OwnerName
                  };

13
不是同一件事情 - 这个问题涉及到根据每个表中的多个列连接表,而不是基于每个表中的单个列连接多个表。 - Isochronous
是的,我不明白这个答案在这里有什么作用。 - Gert Arnold

4

您可以使用LINQ方法语法在多个列上进行连接。这里是一个示例:

var query = mTABLE_1.Join( // mTABLE_1 is a List<TABLE_1>
                mTABLE_1, 
                t1 => new
                {
                    ColA = t1.ColumnA,
                    ColB = t1.ColumnB,
                    ColC = t1.ColumnC
                },
                t2 => new
                {
                    ColA = t2.ColumnA,
                    ColB = t2.ColumnB,
                    ColC = t2.ColumnC
                },
                (t1, t2) => new { t1, t2 }).Join(
                mTABLE_1,
                t1t2 => new
                {
                    ColA = t1t2.t2.ColumnA,
                    ColB = t1t2.t2.ColumnB,
                    ColC = t1t2.t2.ColumnC
                },
                t3 => new
                {
                    ColA = t3.ColumnA,
                    ColB = t3.ColumnB,
                    ColC = t3.ColumnC
                },
                (t1t2, t3) => new
                {
                    t1 = t1t2.t1,
                    t2 = t1t2.t2,
                    t3 = t3
                });

注意:编译器将查询语法在编译时转换为方法语法。


2

如果两个表格的列数不同,您也可以参加并将静态值映射到表格列中。

from t1 in Table1 
join t2 in Table2 
on new {X = t1.Column1, Y = 0 } on new {X = t2.Column1, Y = t2.Column2 }
select new {t1, t2}

0

A和B的别名必须与e表和t表中的Hrco和Position代码相一致,同时也需要在“equal new”筛选器中匹配 Hrco和Position Code组合。这将为您节省时间,因为我之前一直因为错误地认为筛选器是以e.Hrco,t.Hrco为配对而产生了“左侧不在作用域内”的编译错误。

select * from table1 e
   join table2 t on
      e.Hrco=t.Hrco and e.PositionCode=t.PositionCode

   Notice the association of the columns to the labels A and B. The As equal and the Bs equal filter.

   IList<MyView> list = await (from e in _dbContext.table1
                                              join t in _dbContext.table2
                                              on new { A= e.Hrco, B= e.PositionCode }
                                              equals new {A= t.Hrco,B=t.PositionCode }
                                              where e.XMan == employeeNumber

                                              select new MyView
                                                                  { 
                                                                        
         Employee=e.Employee,
         LastName=e.LastName,
         FirstName=e.FirstName,
         Title=t.JobTitle
         ).ToListAsync<MyView>();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接