LINQ to SQL: più join su più colonne. È possibile?


131

Dato:

Una tabella denominata TABLE_1con le seguenti colonne:

  • ID
  • ColumnA
  • ColumnB
  • ColumnC

Ho query SQL in cui TABLE_1si unisce su se stesso due volte in base al largo di ColumnA, ColumnB, ColumnC. La query potrebbe assomigliare a questa:

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:

Ho bisogno che la Query venga riscritta in LINQ. Ho provato a prenderlo a pugni:

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

Come posso scrivere la mia query in LINQ? Che cosa sto facendo di sbagliato?

Risposte:


242

Unire più colonne in Linq a SQL è leggermente diverso.

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

Devi sfruttare i tipi anonimi e comporre un tipo per le colonne multiple che desideri confrontare.

All'inizio questo sembra confuso, ma una volta che avrai familiarizzato con il modo in cui l'SQL è composto dalle espressioni, avrà molto più senso, sotto le copertine questo genererà il tipo di join che stai cercando.

EDIT Aggiunta di esempio per il secondo join in base al commento.

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
funziona alla grande per due join. Ne ho bisogno per funzionare con TRE join. Siamo spiacenti, il secondo blocco di codice è stato un po 'fuorviante.
aarona,

46
Se ricevi un errore del compilatore sull'inferenza del tipo, controlla due cose, (1) sono i tipi uguali e (2) i nomi delle colonne sono uguali. La parte dei nomi è un gotcha. Questo esempio non verrà compilato anche se tutte le colonne sono varchars join T2 in db.tbl2 on new { T1.firstName, T1.secondName } equals new { T2.colFirst, T2.colSecond }. Se lo cambi in questo, verrà comunque compilato,join T2 in db.tbl2 on new { N1 = T1.firstName, N2 = T1.secondName } equals new { N1 = T2.colFirst, N2 = T2.colSecond }
user2023861

4
Il problema di denominazione può essere eliminato da t1 in myTABLE1List join t2 in myTABLE1List su new {colA = t1.ColumnA, colB = t1.ColumnB} è uguale a new {colA = t2.ColumnA, colBBt2.ColumnB}
Naqvi,

1
per favore permettimi di modificare l'esempio, dato che aveva bisogno di assegnazioni a proprietà anonime
AceMark

1
Qualcosa non va qui ... con LINQ. Posso unirmi su più tavoli, posso unirmi su più campi ... tuttavia, non posso farlo per entrambi, come mostra l'esempio qui. Quindi supponi di avere un join su 1 campo .. e hai un secondo join che lo segue. Se si modifica il primo join (o entrambi) per usare solo il nuovo {x.field} è uguale al nuovo {y.field}, si verifica un errore del compilatore. Funzionalmente non hai cambiato nulla. Utilizzo di .Net 4.6.1.
user2415376,

12

In LINQ2SQL raramente è necessario unirsi esplicitamente quando si utilizzano i join interni.

Se nel database sono presenti relazioni di chiave esterna appropriate, si otterrà automaticamente una relazione nella finestra di progettazione LINQ (in caso contrario è possibile creare una relazione manualmente nella finestra di progettazione, anche se in realtà si dovrebbero avere relazioni corrette nel database)

relazione genitore-figlio

Quindi puoi semplicemente accedere alle tabelle correlate con la "notazione punto"

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

genererà la query

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

A mio avviso, questo è molto più leggibile e ti consente di concentrarti sulle tue condizioni speciali e non sui meccanismi effettivi del join.

Modifica
Questo è ovviamente applicabile solo quando si desidera aderire alla linea con il nostro modello di database. Se si desidera unirsi "al di fuori del modello", è necessario ricorrere ai join manuali come nella risposta di Quintin Robinson


11

Title_Authors è una ricerca di due cose che uniscono alla volta i risultati del progetto e continuano a concatenarsi

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

10

Puoi anche usare:

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 }

3
AHHH !! Questo funziona! E la DIFFERENZA CHIAVE, è che devi fare la parte "ColA =" in modo che nell'altro join sia lo stesso campo. Per anni non l'ho fatto, ma avrei anche solo bisogno di 1 join su più campi. Ma ora ho bisogno di più, e FUNZIONA SOLO se assegno un nome di variabile ai campi come in questo esempio.
user2415376,

3

Vorrei fare un altro esempio in cui vengono utilizzati più (3) join.

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

9
Non è la stessa cosa: la domanda riguarda l'unione di tabelle basate su più colonne in ciascuna, non l'unione di più tabelle basate su una singola colonna in ciascuna.
Isochronous

1

Puoi anche unirti se il numero di colonne non è lo stesso in entrambe le tabelle e può mappare il valore statico sulla colonna della tabella

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}

-6

Secondo me, questo è il modo più semplice di unire due tabelle con più campi:

from a in Table1 join b in Table2    
       on (a.Field1.ToString() + "&" + a.Field2.ToString())     
       equals  (b.Field1.ToString() + "&" + b.Field2.ToString())  
     select a

In SQL, ciò sarebbe significativamente più lento rispetto all'unione su ciascuna colonna separatamente (anche se sarebbe comunque piuttosto veloce se il set di dati non fosse grande). Presumibilmente linq genererebbe l'ovvio SQL, quindi tieni a mente le prestazioni se usi questa soluzione.
EGP

-10

Puoi scrivere la tua query in questo modo.

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 vuoi confrontare la tua colonna con più colonne.


1
@ user658720 Benvenuto in StackOverFlow :). Suggerirei di formattare il codice in modo che sia più facile da leggere. Puoi selezionare il testo e fare clic sul pulsante del codice nell'editor.
aarona,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.