LINQ to SQL: jointures multiples sur plusieurs colonnes. Est-ce possible?

131

Donné:

Une table nommée TABLE_1avec les colonnes suivantes:

  • ID
  • ColumnA
  • ColumnB
  • ColumnC

J'ai requête SQL où les TABLE_1jointures sur elle - même basée deux fois au large de ColumnA, ColumnB, ColumnC. La requête peut ressembler à ceci:

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.

Problème:

J'ai besoin que cette requête soit réécrite dans LINQ. J'ai essayé de tenter le coup:

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

Comment écrire ma requête dans LINQ? Qu'est-ce que je fais mal?

Aarona
la source

Réponses:

242

Joindre plusieurs colonnes dans Linq à SQL est un peu différent.

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

Vous devez tirer parti des types anonymes et composer un type pour les multiples colonnes que vous souhaitez comparer.

Cela semble déroutant au début, mais une fois que vous vous serez familiarisé avec la façon dont le SQL est composé à partir des expressions, cela aura beaucoup plus de sens, sous les couvertures, cela générera le type de jointure que vous recherchez.

EDIT Ajout d'un exemple pour la deuxième jointure basée sur un commentaire.

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
la source
4
cela fonctionne très bien pour deux jointures. J'en ai besoin pour fonctionner avec TROIS jointures. Désolé, le deuxième bloc de code était un peu trompeur.
aarona
46
Si vous obtenez une erreur du compilateur concernant l'inférence de type, vérifiez deux choses, (1) les types sont-ils identiques et (2) les noms de colonnes sont les mêmes. La partie des noms est un piège. Cet exemple ne compile pas même si toutes les colonnes sont des varchars join T2 in db.tbl2 on new { T1.firstName, T1.secondName } equals new { T2.colFirst, T2.colSecond }. Si vous le changez, il compilera cependant,join T2 in db.tbl2 on new { N1 = T1.firstName, N2 = T1.secondName } equals new { N1 = T2.colFirst, N2 = T2.colSecond }
user2023861
4
Le problème de dénomination peut être éliminé par from t1 dans myTABLE1List join t2 in myTABLE1List on new {colA = t1.ColumnA, colB = t1.ColumnB} equals new {colA = t2.ColumnA, colBBt2.ColumnB}
Baqer Naqvi
1
s'il vous plaît permettez-moi de modifier l'exemple, car il avait besoin d'attributions à des propriétés anonymes
AceMark
1
Quelque chose ne va pas ici .. avec LINQ. Je peux rejoindre sur plusieurs tables, je peux rejoindre sur plusieurs champs ... cependant, je ne peux pas le faire pour les deux, comme le montre l'exemple ici. Alors disons que vous avez juste une jointure sur 1 champ .. et vous avez une deuxième jointure qui le suit. Si vous modifiez la 1ère jointure (ou les deux) pour n'utiliser que new {x.field} égale new {y.field}, il y a une erreur de compilation. Fonctionnellement, vous n'avez rien changé. Utilisation de .Net 4.6.1.
user2415376
12

Dans LINQ2SQL, vous avez rarement besoin de rejoindre explicitement lorsque vous utilisez des jointures internes.

Si vous avez des relations de clé étrangère appropriées dans votre base de données, vous obtiendrez automatiquement une relation dans le concepteur LINQ (sinon vous pouvez créer une relation manuellement dans le concepteur, bien que vous devriez vraiment avoir des relations appropriées dans votre base de données)

relation parent-enfant

Ensuite, vous pouvez simplement accéder aux tables associées avec la "notation par points"

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

va générer la requête

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

À mon avis, c'est beaucoup plus lisible et vous permet de vous concentrer sur vos conditions spéciales et non sur la mécanique réelle de la jointure.

Modifier
Ceci n'est bien sûr applicable que lorsque vous souhaitez rejoindre la ligne avec notre modèle de base de données. Si vous souhaitez rejoindre "en dehors du modèle", vous devez recourir à des jointures manuelles comme dans la réponse de Quintin Robinson

Albin Sunnanbo
la source
11

Title_Authors est une recherche de deux choses qui se joignent à la fois aux résultats du projet et continuent à enchaîner

        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
la source
10

Vous pouvez également utiliser:

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
la source
3
AHHH !! Cela marche! Et la différence clé, c'est que vous devez faire la partie "ColA =" de sorte que dans l'autre jointure, c'est le même champ. Pendant des années, je ne l'ai pas fait, mais j'aurais également besoin d'une seule inscription sur plusieurs domaines. Mais maintenant, j'ai besoin de plus, et cela fonctionne UNIQUEMENT si j'attribue un nom de variable aux champs comme dans cet exemple.
user2415376
3

Je voudrais donner un autre exemple dans lequel plusieurs jointures (3) sont utilisées.

 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
la source
9
Pas la même chose - la question est de joindre des tables basées sur plusieurs colonnes dans chacune, et non de joindre plusieurs tables basées sur une seule colonne dans chacune.
Isochronous le
1

Vous pouvez également rejoindre si le nombre de colonnes n'est pas le même dans les deux tables et peut mapper une valeur statique à la colonne de table

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
la source
-6

À mon avis, c'est le moyen le plus simple de joindre deux tables avec plusieurs champs:

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
la source
En SQL, cela serait beaucoup plus lent que de joindre séparément chaque colonne (bien que ce soit toujours assez rapide si le jeu de données n'est pas volumineux). Vraisemblablement, linq générerait le SQL évident, alors gardez à l'esprit les performances si vous utilisez cette solution.
EGP
-10

Vous pouvez rédiger votre requête comme ceci.

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

Si vous souhaitez comparer votre colonne avec plusieurs colonnes.

Anvesh
la source
1
@ user658720 Bienvenue dans StackOverFlow :). Je vous suggère de formater votre code afin qu'il soit plus facile à lire. Vous pouvez sélectionner le texte et cliquer sur le bouton de code dans l'éditeur.
aarona