LINQ to SQL: wiele sprzężeń na wielu kolumnach. czy to możliwe?

131

Dany:

Tabela o nazwie TABLE_1zawierającej następujące kolumny:

  • ID
  • ColumnA
  • ColumnB
  • ColumnC

Mam kwerendy SQL, gdzie TABLE_1łączy się na siebie dwa razy w oparciu off ColumnA, ColumnB, ColumnC. Zapytanie może wyglądać mniej więcej tak:

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.

Problem:

Potrzebuję przepisania tego zapytania w LINQ. Próbowałem go dźgnąć:

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

Jak napisać zapytanie w LINQ? Co ja robię źle?

aarona
źródło

Odpowiedzi:

242

Łączenie wielu kolumn w Linq do SQL wygląda trochę inaczej.

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

Musisz skorzystać z anonimowych typów i utworzyć typ dla wielu kolumn, z którymi chcesz porównać.

Na początku wydaje się to zagmatwane, ale gdy zapoznasz się ze sposobem, w jaki SQL składa się z wyrażeń, będzie to miało o wiele więcej sensu, pod okładkami wygeneruje to typ sprzężenia, którego szukasz.

EDYCJA Dodanie przykładu dla drugiego sprzężenia na podstawie komentarza.

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
źródło
4
to działa świetnie dla dwóch złączeń. Potrzebuję go do pracy z TRZYMI połączeniami. Przepraszamy, drugi blok kodu był trochę mylący.
aarona
46
Jeśli pojawi się błąd kompilatora dotyczący wnioskowania o typie, sprawdź dwie rzeczy: (1) to te same typy, a (2) to takie same nazwy kolumn. Część z nazwiskami to problem. Ten przykład nie zostanie skompilowany, nawet jeśli wszystkie kolumny są varchars join T2 in db.tbl2 on new { T1.firstName, T1.secondName } equals new { T2.colFirst, T2.colSecond }. Jeśli zmienisz to na to, będzie się kompilować,join T2 in db.tbl2 on new { N1 = T1.firstName, N2 = T1.secondName } equals new { N1 = T2.colFirst, N2 = T2.colSecond }
user2023861
4
Problem z nazewnictwem można wyeliminować za pomocą t1 w myTABLE1List join t2 w myTABLE1List na nowej {colA = t1.ColumnA, colB = t1.ColumnB} równa się new {colA = t2.ColumnA, colBBt2.ColumnB}
Baqer Naqvi
1
proszę pozwolić mi edytować przykład, ponieważ wymagał on przypisania do anonimowych nieruchomości
AceMark
1
Coś tu jest nie tak ... z LINQ. Mogę dołączać na wielu stołach, mogę dołączać na wielu polach ... jednak nie mogę tego zrobić dla obu, jak pokazuje przykład tutaj. Powiedzmy, że po prostu masz złączenie na 1 polu… i drugie złączenie po nim. Jeśli zmienisz pierwsze sprzężenie (lub oba), aby po prostu użyć nowego {x.field} równa się nowe {y.field}, wystąpi błąd kompilatora. Funkcjonalnie niczego nie zmieniłeś. Korzystanie z .Net 4.6.1.
user2415376
12

W LINQ2SQL rzadko trzeba jawnie łączyć się podczas korzystania ze sprzężeń wewnętrznych.

Jeśli masz odpowiednie relacje klucza obcego w swojej bazie danych, automatycznie uzyskasz relację w projektancie LINQ (jeśli nie, możesz ręcznie utworzyć relację w projektancie, chociaż naprawdę powinieneś mieć odpowiednie relacje w swojej bazie danych)

relacja rodzic-dziecko

Następnie możesz po prostu uzyskać dostęp do powiązanych tabel za pomocą „notacji z kropką”

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

wygeneruje zapytanie

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

Moim zdaniem jest to dużo bardziej czytelne i pozwala skoncentrować się na specjalnych warunkach, a nie na faktycznej mechanice łączenia.

Edytuj
Ma to oczywiście zastosowanie tylko wtedy, gdy chcesz dołączyć do linii z naszym modelem bazy danych. Jeśli chcesz dołączyć do „poza modelem” trzeba uciekać się do instrukcji łączy jak w odpowiedzi od Quintin Robinson

Albin Sunnanbo
źródło
11

Title_Authors to wyszukiwanie dwóch rzeczy, które łączą się w czasie z wynikami projektu i kontynuują łączenie

        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
źródło
10

Możesz również użyć:

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
źródło
3
AHHH !! To działa! A KLUCZOWA RÓŻNICA polega na tym, że musisz wykonać część „ColA =”, tak aby w drugim złączeniu było to samo pole. Przez lata tego nie robiłem, ale potrzebowałbym tylko 1 dołączenia na wielu polach. Ale teraz potrzebuję więcej i DZIAŁA TYLKO, jeśli przypiszę nazwę zmiennej do pól, jak w tym przykładzie.
user2415376
3

Chciałbym podać inny przykład, w którym zastosowano wielokrotne (3) łączenia.

 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
źródło
9
To nie to samo - pytanie dotyczy łączenia tabel na podstawie wielu kolumn w każdej, a nie łączenia wielu tabel na podstawie jednej kolumny w każdej.
Izochroniczny
1

Możesz również dołączyć, jeśli liczba kolumn nie jest taka sama w obu tabelach i możesz odwzorować wartość statyczną na kolumnę tabeli

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
źródło
-6

Moim zdaniem jest to najprostszy sposób na połączenie dwóch tabel z wieloma polami:

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
źródło
W SQL wykonanie tego byłoby znacznie wolniejsze niż dołączanie do każdej kolumny osobno (choć nadal byłoby dość szybkie, gdyby zbiór danych nie był duży). Przypuszczalnie linq wygenerowałoby oczywiste SQL, więc pamiętaj o wydajności, jeśli używasz tego rozwiązania.
EGP
-10

Możesz napisać swoje zapytanie w ten sposób.

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

Jeśli chcesz porównać swoją kolumnę z wieloma kolumnami.

Anvesh
źródło
1
@ user658720 Witamy w StackOverFlow :). Proponuję sformatować kod tak, aby był łatwiejszy do odczytania. Możesz zaznaczyć tekst i kliknąć przycisk kodu w edytorze.
aarona