Come eseguire join in LINQ su più campi in un unico join


244

Ho bisogno di fare una query LINQ2DataSet che faccia un join su più di un campo (come

var result = from x in entity
join y in entity2 
       on x.field1 = y.field1 
and 
          x.field2 = y.field2

Ho ancora trovato una soluzione adatta (posso aggiungere ulteriori vincoli a una clausola where, ma questa è ben lungi dall'essere una soluzione adatta, oppure utilizzare questa soluzione, ma che presuppone un equijoin).

È possibile in LINQ unirsi su più campi in un singolo join?

MODIFICARE

var result = from x in entity
             join y in entity2
             on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

è la soluzione a cui ho fatto riferimento assumendo un equijoin sopra.

Ulteriore modifica

Per rispondere alle critiche secondo cui il mio esempio originale era un equijoin, riconosco che, il mio attuale requisito è per un equijoin e ho già utilizzato la soluzione di cui sopra.

Sto, tuttavia, cercando di capire quali possibilità e migliori pratiche ho / dovrei utilizzare con LINQ. Presto avrò bisogno di fare una query su un intervallo di date con un ID tabella e sto solo anticipando quel problema. Sembra che dovrò aggiungere l'intervallo di date nella clausola where.

Grazie, come sempre, per tutti i suggerimenti e commenti forniti


48
Solo un FYI per chiunque legga questo, se fai un join multi-campo nelle classi annon, DEVI nominare i campi in entrambe le classi annon uguali, altrimenti otterrai errori di compilazione.
Segna il

6
O meglio, devi assicurarti che abbiano nomi corrispondenti. cioè puoi semplicemente nominare i campi di uno dei tipi di anone per farli corrispondere all'altro.
Tom Ferguson,

1
Prestare attenzione a questa risposta stackoverflow.com/a/34176502/1704458
TS

Ho usato le tuple per entrambi i lati degli uguali anziché gli oggetti e sembrava funzionare anche.
GHZ

Risposte:


89

La soluzione con il tipo anonimo dovrebbe funzionare correttamente. LINQ può rappresentare solo equijoin (con clausole di join, comunque), e in effetti è quello che hai detto che vuoi esprimere comunque in base alla tua query originale.

Se non ti piace la versione con il tipo anonimo per qualche motivo specifico, dovresti spiegarlo.

Se vuoi fare qualcosa di diverso da quello che hai chiesto inizialmente, ti preghiamo di fare un esempio di ciò che vuoi davvero fare.

EDIT: rispondendo alla modifica nella domanda: sì, per fare un join "intervallo di date", devi usare invece una clausola where. Sono davvero semanticamente equivalenti, quindi è solo una questione di ottimizzazioni disponibili. Gli equijoin forniscono una semplice ottimizzazione (in LINQ to Objects, che include LINQ to DataSet) creando una ricerca basata sulla sequenza interna - pensala come una tabella hash dalla chiave a una sequenza di voci corrispondenti a quella chiave.

Farlo con intervalli di date è un po 'più difficile. Tuttavia, a seconda di cosa si intende esattamente per "unione di intervalli di date", potresti essere in grado di fare qualcosa di simile - se stai pianificando di creare "fasce" di date (ad esempio una all'anno) in modo tale che due voci presenti nel lo stesso anno (ma non nella stessa data) dovrebbe corrispondere, quindi puoi farlo semplicemente usando quella banda come chiave. Se è più complicato, ad esempio un lato del join fornisce un intervallo e l'altro lato del join fornisce una singola data, corrispondente se rientra in tale intervallo, che sarebbe meglio gestita con una whereclausola (dopo un secondofromclausola) IMO. Potresti fare qualche magia particolarmente funky ordinando da una parte o dall'altra di trovare le partite in modo più efficiente, ma sarebbe un sacco di lavoro - farei quel tipo di cose solo dopo aver verificato se le prestazioni sono un problema.


Grazie, sì, le prestazioni sono la mia principale preoccupazione nell'uso della clausola where. Immagino che una clausola where dopo il join esegua un filtro su un set di dati più grande che potrebbe essere stato ridotto introducendo il secondo parametro join. Mi piace l'idea di ordinare di provare se posso ottenere incrementi di efficienza
johnc

Quanti record avrai? Non dimenticare che ordinare i risultati per iniziare richiederà un certo tempo per iniziare ...
Jon Skeet,

"Sono davvero semanticamente equivalenti" - abbiamo bisogno della parola "davvero" lì dentro? Forse intendevi "Sono davvero semanticamente equivalenti" :)
giorno

136
var result = from x in entity
   join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

Questo è quello che stavo cercando visto che i 101 Linq Samples non avevano questo o almeno quello che ho visto.
Chris Marisic,

1
@PeterX anzi può, vedere la mia risposta qui: stackoverflow.com/a/22176658/595157
niieani

13
Il codice sopra non ha funzionato. Dopo averlo aggiunto ha on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 } funzionato
Ravi Ram il

@Ravi Ram .. Grazie .. il tuo commento ha aiutato
NMathur

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

Devi farlo, se i nomi delle colonne sono diversi in due entità.


6
Grazie per aver menzionato i nomi delle diverse colonne. Questo risolto la mia cattiva espressione.
Gaʀʀʏ

1
Questo ha funzionato anche per me. Se i nomi delle colonne non corrispondono, verrà visualizzato questo errore, "Il tipo di una delle espressioni nella clausola join non è corretto. L'inferenza del tipo non è riuscita nella chiamata a 'GroupJoin'."
humbads

Grazie per aver aliasato le variabili chiave.
Thomas.Benz,

Ho ricevuto l'errore che @humbads ha menzionato quando non ho dato un nome a tutte le proprietà di "new {}". Quindi solo se ne chiami uno devi anche nominare il resto.
Ethan Melamed,

GRAZIE TANTO
Charly H,

51

Solo per completare questo con una sintassi della catena del metodo equivalente:

entity.Join(entity2, x => new {x.Field1, x.Field2},
                     y => new {y.Field1, y.Field2}, (x, y) => x);

Mentre l'ultimo argomento (x, y) => xè quello che selezioni (nel caso precedente selezioniamo x).


31

Penso che un'opzione più leggibile e flessibile sia quella di utilizzare la funzione Where:

var result = from x in entity1
             from y in entity2
                 .Where(y => y.field1 == x.field1 && y.field2 == x.field2)

Ciò consente anche di passare facilmente dal join interno al join sinistro aggiungendo .DefaultIfEmpty ().


Come utente lambda da molto tempo ora (al contrario di quando ho posto la domanda), dovrei essere d'accordo
johnc,

Questo sarebbe più lento?
AlfredBr,

1
Penso che dovrebbe avere le stesse prestazioni della nuova { ... } equals new { ... }sintassi. LinqPad è un ottimo strumento per vedere come si comportano le espressioni (script SQL se si utilizza LINQ2SQL, alberi delle espressioni ecc.)
Alexei

Per quanto ho notato, sta producendo CROSS JOIN invece di INNER JOIN
Mariusz il

@Mariusz Sì, ha senso generare CROSS JOIN + DOVE invece di INNER JOIN. Per semplici domande, tuttavia, mi aspetto che l'analizzatore generi qualcosa di molto simile.
Alexei,

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

8

potresti fare qualcosa del tipo (sotto)

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

Come ho detto in questione, ciò richiede un equijoin
johnc,

7

Utilizzando l'operatore join è possibile eseguire solo equijoin. Altri tipi di join possono essere costruiti utilizzando altri operatori. Non sono sicuro che il join esatto che si sta tentando di eseguire sia più semplice utilizzando questi metodi o modificando la clausola where. La documentazione sulla clausola di join è disponibile qui . MSDN ha anche un articolo sulle operazioni di join con più collegamenti ad esempi di altri join.


3

Se il nome del campo è diverso nelle entità

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

Grazie. La corrispondenza dei nomi era il pezzo che mi mancava.
Brett,

2

Come una catena di metodi completa che sarebbe simile a questa:

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();

-2
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)

questo funziona per me


Questo è per un join multiplo, vuole fare un join con più campi in un unico join
theLaw

-3

Dichiarare una classe (tipo) per contenere gli elementi a cui si desidera unirsi. Nell'esempio seguente dichiarare 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

1
Questa risposta è già stata risolta 9 anni fa ... che valore ha questa risposta?
Maciej Jureczko,
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.