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

Posted

技术标签:

【中文标题】LINQ to SQL:多个列上的多个连接。这可能吗?【英文标题】:LINQ to SQL: Multiple joins ON multiple Columns. Is this possible? 【发布时间】:2011-07-15 12:43:56 【问题描述】:

鉴于:

一个名为 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 中编写查询?我做错了什么?

【问题讨论】:

【参考方案1】:

在 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 
    ...

【讨论】:

这适用于两个连接。我需要它与三个连接一起工作。抱歉,第二个代码块有点误导。 如果您收到关于类型推断的编译器错误,请检查两件事,(1) 类型是否相同,(2) 列名是否相同。名称部分是一个陷阱。即使所有列都是 varchars 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 命名问题可以通过 from t1 in myTABLE1List join t2 in myTABLE1List on new colA=t1.ColumnA, colB=t1.ColumnB equals new colA=t2.ColumnA, colBBt2.ColumnB 请允许我编辑示例,因为它需要分配给匿名属性 这里出了点问题.. 使用 LINQ。我可以加入多个表,我可以加入多个字段......但是,我不能同时做这两个,如示例所示here。所以说你只是在 1 个字段上加入了......并且你有一个跟随它的第二个加入。如果您将第一个连接(或两者)更改为仅使用 new x.field equals new y.field ,则会出现编译器错误。从功能上讲,你没有改变任何东西。使用 .Net 4.6.1。【参考方案2】:

你也可以使用:

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 

【讨论】:

啊啊啊!!这行得通!关键的区别在于,您需要执行“ColA =”部分,以便在另一个加入中它是相同的字段。多年来我没有这样做,但也只需要在多个字段上加入 1 个。但是现在我需要更多,而且只有在我为这个例子中的字段分配一个变量名时它才有效。【参考方案3】:

在 LINQ2SQL 中,使用内连接时很少需要显式连接。

如果您的数据库中有正确的外键关系,您将在 LINQ 设计器中自动获得一个关系(如果没有,您可以在设计器中手动创建一个关系,尽管您的数据库中应该有正确的关系)

然后您可以使用“点符号”访问相关表

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

在我看来,这更具可读性,可以让您专注于您的特殊条件,而不是连接的实际机制。

编辑 这当然只适用于您想加入符合我们的数据库模型的情况。如果您想在“模型外”加入,则需要像 answer 中的 Quintin Robinson 中的手动加入

【讨论】:

【参考方案4】:

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;
        

【讨论】:

【参考方案5】:

我想再举一个使用多 (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
                  ;

【讨论】:

不是一回事 - 问题是关于基于每个列中的多个列连接表,而不是基于每个列中的单个列连接多个表。 是的,我看不出这个答案在这里做了什么。【参考方案6】:

如果两个表中的列数不相同,也可以加入,并且可以将静态值映射到表列

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

【讨论】:

【参考方案7】:

您可以使用 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
                );

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

【讨论】:

【参考方案8】:

A 和 B 别名必须与 e 表和 t 表中的 Hrco 和 Position 代码对齐 - “equal new”过滤器中的 Hrco 和 Position 代码组合。这将节省您的时间,因为我一直收到“不在左侧范围内”的编译错误,因为我认为过滤器是 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>();

【讨论】:

以上是关于LINQ to SQL:多个列上的多个连接。这可能吗?的主要内容,如果未能解决你的问题,请参考以下文章

Linq to Sql:多个左外连接

Linq to SQL 使用 Lambda 语法进行左外连接并在 2 列上连接(复合连接键)

sql join 与列上的多个条件

在EF中的多个列上连接多个表

LINQ to SQL:对来自订购系统的多个表的报告的聚合数据进行复杂查询

C# - 多个属性上的动态 Linq 左外连接