LINQ to SQL: várias junções em várias colunas. Isso é possível?

131

Dado:

Uma tabela nomeada TABLE_1com as seguintes colunas:

  • ID
  • ColumnA
  • ColumnB
  • ColumnC

Eu tenho consulta SQL, onde TABLE_1se une em si mesmo duas vezes baseado fora de ColumnA, ColumnB, ColumnC. A consulta pode ser algo como isto:

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.

Problema:

Eu preciso que essa consulta seja reescrita no LINQ. Eu tentei dar uma facada nele:

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 :(

Como escrevo minha consulta no LINQ? O que estou fazendo de errado?

aarona
fonte

Respostas:

242

A junção de várias colunas no Linq to SQL é um pouco diferente.

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

Você precisa aproveitar os tipos anônimos e compor um tipo para as várias colunas com as quais deseja comparar.

Isso parece confuso no começo, mas quando você se familiarizar com a maneira como o SQL é composto a partir das expressões, fará muito mais sentido; por baixo das cobertas, isso gerará o tipo de junção que você está procurando.

EDIT Adicionando exemplo para a segunda junção com base no comentário.

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 }
    ...
Quintin Robinson
fonte
4
isso funciona muito bem para duas junções. Eu preciso que ele trabalhe com TRÊS junções. Desculpe, o segundo bloco de código foi um pouco enganador.
AaronA
46
Se você receber um erro do compilador sobre a inferência de tipo, verifique duas coisas, (1) são os mesmos e (2) os nomes das colunas são iguais. A parte dos nomes é uma pegadinha. Este exemplo não será compilado, mesmo que todas as colunas sejam varchars join T2 in db.tbl2 on new { T1.firstName, T1.secondName } equals new { T2.colFirst, T2.colSecond }. Se você alterá-lo para isso, ele será compilado, no entanto,join T2 in db.tbl2 on new { N1 = T1.firstName, N2 = T1.secondName } equals new { N1 = T2.colFirst, N2 = T2.colSecond }
user2023861 3/13
4
Problema nomeando pode ser eliminado a partir de t1 em myTABLE1List juntar-se t2 em myTABLE1List na nova {colA = t1.ColumnA, colB = t1.ColumnB} é igual a nova {colA = t2.ColumnA, colBBt2.ColumnB}
Baqer Naqvi
1
permita-me editar o exemplo, pois precisava atribuições para propriedades anônimos
AceMark
1
Algo está errado aqui .. com LINQ. Posso entrar em várias tabelas, posso entrar em vários campos ... no entanto, não posso fazê-lo para ambos, como o exemplo mostra aqui. Digamos que você tenha uma junção em um campo .. e você terá uma segunda junção a seguir. Se você alterar a 1ª junção (ou ambas) para usar apenas novo {x.field} igual a novo {y.field}, ocorrerá um erro do compilador. Funcionalmente, você não mudou nada. Usando .Net 4.6.1.
user2415376
12

No LINQ2SQL, você raramente precisa ingressar explicitamente ao usar junções internas.

Se você tiver relacionamentos adequados de chave estrangeira no seu banco de dados, obterá automaticamente uma relação no designer do LINQ (caso contrário, poderá criar uma relação manualmente no designer, embora você realmente deva ter relações adequadas no banco de dados)

relação pai-filho

Então você pode simplesmente acessar as tabelas relacionadas com a "notação de ponto"

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

irá gerar a consulta

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

Na minha opinião, isso é muito mais legível e permite que você se concentre em suas condições especiais e não na mecânica real da junção.

Editar
Naturalmente, isso só é aplicável quando você deseja ingressar na linha com nosso modelo de banco de dados. Se você deseja participar "fora do modelo", precisará recorrer a junções manuais, como na resposta de Quintin Robinson

Albin Sunnanbo
fonte
11

Title_Authors é procurar duas coisas que se juntam ao mesmo tempo aos resultados do projeto e continuar encadeando

        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;
        }
BionicCyborg
fonte
10

Você também pode usar:

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 }
Baqer Naqvi
fonte
3
AHHH!! Isso funciona! E a DIFERENÇA CHAVE é que você precisa fazer a parte "ColA =" para que na outra junção seja o mesmo campo. Durante anos eu não fiz isso, mas também precisaria de apenas 1 associação em vários campos. Mas agora eu preciso de mais, e só funciona se eu atribuir um nome de variável aos campos, como neste exemplo.
user2415376
3

Eu gostaria de dar outro exemplo no qual várias (3) junções são usadas.

 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
                  };
user3477428
fonte
9
Não é a mesma coisa - a questão é unir tabelas com base em várias colunas em cada uma, não unir várias tabelas com base em uma única coluna em cada uma.
Isócrono 04/09
1

Você também pode ingressar se o número de colunas não for o mesmo nas duas tabelas e puder mapear o valor estático para a coluna da tabela

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}
Ankit Arya
fonte
-6

Na minha opinião, esta é a maneira mais simples de juntar duas tabelas com vários campos:

from a in Table1 join b in Table2    
       on (a.Field1.ToString() + "&" + a.Field2.ToString())     
       equals  (b.Field1.ToString() + "&" + b.Field2.ToString())  
     select a
Praveen Kumar
fonte
No SQL, isso seria significativamente mais lento do que a junção em cada coluna separadamente (embora ainda fosse bastante rápido se o conjunto de dados não fosse grande). Presumivelmente, o linq geraria o SQL óbvio, portanto, lembre-se do desempenho se você usar esta solução.
EGP
-10

Você pode escrever sua consulta assim.

var query = from t1 in myTABLE1List // List<TABLE_1>
            join t2 in myTABLE1List
               on t1.ColumnA equals t2.ColumnA
               and t1.ColumnB equals t2.ColumnA

Se você deseja comparar sua coluna com várias colunas.

Anvesh
fonte
1
@ user658720 Bem-vindo ao StackOverFlow :). Eu sugiro que você formate o código para que seja mais fácil de ler. Você pode selecionar o texto e clicar no botão de código no editor.
AaronA