join, clause (référence C#)join clause (C# Reference)

La clause join est utile pour associer des éléments de différentes séquences sources qui n’ont pas de relation directe dans le modèle objet.The join clause is useful for associating elements from different source sequences that have no direct relationship in the object model. Le seul impératif est que les éléments de chaque source partagent une valeur dont l’égalité peut être comparée.The only requirement is that the elements in each source share some value that can be compared for equality. Par exemple, un distributeur de produits alimentaires peut avoir une liste de fournisseurs d’un certain produit et une liste d’acheteurs.For example, a food distributor might have a list of suppliers of a certain product, and a list of buyers. Une clause join peut être utilisée par exemple pour créer une liste des fournisseurs et des acheteurs de ce produit qui se trouvent tous dans la même région spécifiée.A join clause can be used, for example, to create a list of the suppliers and buyers of that product who are all in the same specified region.

Une clause join prend deux séquences sources comme entrée.A join clause takes two source sequences as input. Les éléments de chaque séquence doivent être ou contenir une propriété qui peut être comparée à une propriété correspondante dans l’autre séquence.The elements in each sequence must either be or contain a property that can be compared to a corresponding property in the other sequence. La clause join utilise le mot clé spécial equals pour comparer les clés spécifiées et déterminer si elles sont égales.The join clause compares the specified keys for equality by using the special equals keyword. Toutes les jointures effectuées par la clause join sont des équijointures.All joins performed by the join clause are equijoins. La forme de la sortie d’une clause join dépend du type spécifique de jointure que vous effectuez.The shape of the output of a join clause depends on the specific type of join you are performing. Les trois types de jointure les plus courants sont les suivants :The following are three most common join types:

  • Jointure interneInner join

  • Jointure groupéeGroup join

  • Jointure externe gaucheLeft outer join

Jointure interneInner join

L’exemple suivant montre une équijointure interne simple.The following example shows a simple inner equijoin. Cette requête produit une séquence plate de paires « nom de produit / catégorie ».This query produces a flat sequence of "product name / category" pairs. La même chaîne de catégorie apparaît dans plusieurs éléments.The same category string will appear in multiple elements. Si un élément de categories n’a pas de products correspondant, cette catégorie n’apparaît pas dans les résultats.If an element from categories has no matching products, that category will not appear in the results.

var innerJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID
    select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence

Pour plus d’informations, consultez Effectuer des jointures internes.For more information, see Perform inner joins.

Jointure groupéeGroup join

Une clause join avec une expression into est appelée une jointure groupée.A join clause with an into expression is called a group join.

var innerGroupJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    select new { CategoryName = category.Name, Products = prodGroup };

Une jointure groupée produit une séquence de résultats hiérarchique, qui associe des éléments de la séquence source de gauche à un ou plusieurs éléments correspondants de la séquence source de droite.A group join produces a hierarchical result sequence, which associates elements in the left source sequence with one or more matching elements in the right side source sequence. Une jointure groupée n’a pas d’équivalent en termes relationnels ; il s’agit en fait d’une séquence de tableaux d’objets.A group join has no equivalent in relational terms; it is essentially a sequence of object arrays.

Si aucun élément de la séquence source de droite n’est trouvé en correspondance avec un élément de la source de gauche, la clause join produit un tableau vide pour cet élément.If no elements from the right source sequence are found to match an element in the left source, the join clause will produce an empty array for that item. Par conséquent, la jointure groupée est fondamentalement une équijointure interne, excepté que la séquence de résultats est organisée en groupes.Therefore, the group join is still basically an inner-equijoin except that the result sequence is organized into groups.

Si vous sélectionnez les résultats d’une jointure groupée, vous pouvez accéder aux éléments, mais vous ne pouvez pas identifier leur clé de correspondance.If you just select the results of a group join, you can access the items, but you cannot identify the key that they match on. Par conséquent, il est généralement plus utile de sélectionner les résultats de la jointure groupée dans un nouveau type qui a aussi le nom de clé, comme illustré dans l’exemple précédent.Therefore, it is generally more useful to select the results of the group join into a new type that also has the key name, as shown in the previous example.

Bien sûr, vous pouvez aussi utiliser le résultat d’une jointure groupée comme générateur d’une autre sous-requête :You can also, of course, use the result of a group join as the generator of another subquery:

var innerGroupJoinQuery2 =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from prod2 in prodGroup
    where prod2.UnitPrice > 2.50M
    select prod2;

Pour plus d’informations, consultez Effectuer des jointures groupées.For more information, see Perform grouped joins.

Jointure externe gaucheLeft outer join

Dans une jointure externe gauche, tous les éléments de la séquence source de gauche sont retournés, même si aucun élément correspondant ne se trouve dans la séquence de droite.In a left outer join, all the elements in the left source sequence are returned, even if no matching elements are in the right sequence. Pour effectuer une jointure extérieure gauche DefaultIfEmpty dans LINQ, utilisez la méthode en combinaison avec une jointure de groupe pour spécifier un élément latéral droit par défaut à produire si un élément du côté gauche n’a pas de correspondances.To perform a left outer join in LINQ, use the DefaultIfEmpty method in combination with a group join to specify a default right-side element to produce if a left-side element has no matches. Vous pouvez utiliser null comme valeur par défaut pour tous les types référence ou vous pouvez spécifier un type par défaut défini par l’utilisateur.You can use null as the default value for any reference type, or you can specify a user-defined default type. L’exemple suivant montre un type par défaut défini par l’utilisateur :In the following example, a user-defined default type is shown:

var leftOuterJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from item in prodGroup.DefaultIfEmpty(new Product { Name = String.Empty, CategoryID = 0 })
    select new { CatName = category.Name, ProdName = item.Name };

Pour plus d’informations, consultez Effectuer des jointures externes gauches.For more information, see Perform left outer joins.

Opérateur ÉgalThe equals operator

Une clause join effectue une équijointure.A join clause performs an equijoin. En d’autres termes, vous pouvez baser les correspondances seulement sur l’égalité de deux clés.In other words, you can only base matches on the equality of two keys. Les autres types de comparaisons, comme « supérieur à » ou « différent de », ne sont pas pris en charge.Other types of comparisons such as "greater than" or "not equals" are not supported. Pour indiquer clairement que toutes les jointures sont des équijointures, la clause join utilise le mot clé equals au lieu de l’opérateur ==.To make clear that all joins are equijoins, the join clause uses the equals keyword instead of the == operator. Le mot clé equals peut être utilisé seulement dans une clause join et diffère de l’opérateur == sur un point important.The equals keyword can only be used in a join clause and it differs from the == operator in one important way. Avec equals, la clé gauche consomme la séquence source externe et la clé droite consomme la source interne.With equals, the left key consumes the outer source sequence, and the right key consumes the inner source. La source externe est seulement dans l’étendue du côté gauche de equals et la séquence source interne est seulement dans l’étendue du côté droit.The outer source is only in scope on the left side of equals and the inner source sequence is only in scope on the right side.

Non-équijointuresNon-equijoins

Vous pouvez effectuer des non-équijointures, des jointures croisées et d’autres opérations de jointure personnalisées en utilisant plusieurs clauses from pour introduire indépendamment de nouvelles séquences dans une requête.You can perform non-equijoins, cross joins, and other custom join operations by using multiple from clauses to introduce new sequences independently into a query. Pour plus d’informations, consultez Effectuer des opérations de jointure personnalisées.For more information, see Perform custom join operations.

Jointures sur des collections d’objets et sur des tables relationnellesJoins on object collections vs. relational tables

Dans une expression de requête LINQ, les opérations de jointure sont effectuées sur les collections d’objets.In a LINQ query expression, join operations are performed on object collections. Les collections d’objets ne peuvent pas être « jointes » exactement de la même façon que deux tables relationnelles.Object collections cannot be "joined" in exactly the same way as two relational tables. Dans LINQ, join les clauses explicites ne sont requises que lorsque deux séquences sources ne sont liées par aucune relation.In LINQ, explicit join clauses are only required when two source sequences are not tied by any relationship. Quand vous travaillez avec LINQ to SQLLINQ to SQL, les tables avec des clés étrangères sont représentées dans le modèle objet en tant que propriétés de la table principale.When working with LINQ to SQLLINQ to SQL, foreign key tables are represented in the object model as properties of the primary table. Par exemple, dans la base de données Northwind, la table Customer a une relation de clé étrangère avec la table Orders.For example, in the Northwind database, the Customer table has a foreign key relationship with the Orders table. Quand vous mappez les tables au modèle objet, la classe Customer a une propriété Orders qui contient la collection de commandes associées à ce client.When you map the tables to the object model, the Customer class has an Orders property that contains the collection of Orders associated with that Customer. En réalité, la jointure a déjà été effectuée pour vous.In effect, the join has already been done for you.

Pour plus d’informations sur l’interrogation de tables liées dans le contexte de LINQ to SQLLINQ to SQL, consultez Procédure : mapper des relations de base de données.For more information about querying across related tables in the context of LINQ to SQLLINQ to SQL, see How to: Map Database Relationships.

Clés compositesComposite keys

Vous pouvez tester l’égalité de plusieurs valeurs en utilisant une clé composite.You can test for equality of multiple values by using a composite key. Pour plus d’informations, consultez Effectuer des opérations de jointure à l’aide de clés composites.For more information, see Join by using composite keys. Vous pouvez aussi utiliser des clés composites dans une clause group.Composite keys can be also used in a group clause.

 ExempleExample

L’exemple suivant compare les résultats d’une jointure interne, d’une jointure groupée et d’une jointure externe gauche sur les mêmes sources de données en utilisant les mêmes clés de correspondance.The following example compares the results of an inner join, a group join, and a left outer join on the same data sources by using the same matching keys. Du code supplémentaire est ajouté à ces exemples pour clarifier les résultats dans l’affichage de la console.Some extra code is added to these examples to clarify the results in the console display.

class JoinDemonstration
{
    #region Data

    class Product
    {
        public string Name { get; set; }
        public int CategoryID { get; set; }
    }

    class Category
    {
        public string Name { get; set; }
        public int ID { get; set; }
    }

    // Specify the first data source.
    List<Category> categories = new List<Category>()
    {
        new Category {Name="Beverages", ID=001},
        new Category {Name="Condiments", ID=002},
        new Category {Name="Vegetables", ID=003},
        new Category {Name="Grains", ID=004},
        new Category {Name="Fruit", ID=005}
    };

    // Specify the second data source.
    List<Product> products = new List<Product>()
   {
      new Product {Name="Cola",  CategoryID=001},
      new Product {Name="Tea",  CategoryID=001},
      new Product {Name="Mustard", CategoryID=002},
      new Product {Name="Pickles", CategoryID=002},
      new Product {Name="Carrots", CategoryID=003},
      new Product {Name="Bok Choy", CategoryID=003},
      new Product {Name="Peaches", CategoryID=005},
      new Product {Name="Melons", CategoryID=005},
    };
    #endregion

    static void Main(string[] args)
    {
        JoinDemonstration app = new JoinDemonstration();

        app.InnerJoin();
        app.GroupJoin();
        app.GroupInnerJoin();
        app.GroupJoin3();
        app.LeftOuterJoin();
        app.LeftOuterJoin2();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    void InnerJoin()
    {
        // Create the query that selects
        // a property from each element.
        var innerJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID
           select new { Category = category.ID, Product = prod.Name };

        Console.WriteLine("InnerJoin:");
        // Execute the query. Access results
        // with a simple foreach statement.
        foreach (var item in innerJoinQuery)
        {
            Console.WriteLine("{0,-10}{1}", item.Product, item.Category);
        }
        Console.WriteLine("InnerJoin: {0} items in 1 group.", innerJoinQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void GroupJoin()
    {
        // This is a demonstration query to show the output
        // of a "raw" group join. A more typical group join
        // is shown in the GroupInnerJoin method.
        var groupJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup;

        // Store the count of total items (for demonstration only).
        int totalItems = 0;

        Console.WriteLine("Simple GroupJoin:");

        // A nested foreach statement is required to access group items.
        foreach (var prodGrouping in groupJoinQuery)
        {
            Console.WriteLine("Group:");
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("   {0,-10}{1}", item.Name, item.CategoryID);
            }
        }
        Console.WriteLine("Unshaped GroupJoin: {0} items in {1} unnamed groups", totalItems, groupJoinQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void GroupInnerJoin()
    {
        var groupJoinQuery2 =
            from category in categories
            orderby category.ID
            join prod in products on category.ID equals prod.CategoryID into prodGroup
            select new
            {
                Category = category.Name,
                Products = from prod2 in prodGroup
                           orderby prod2.Name
                           select prod2
            };

        //Console.WriteLine("GroupInnerJoin:");
        int totalItems = 0;

        Console.WriteLine("GroupInnerJoin:");
        foreach (var productGroup in groupJoinQuery2)
        {
            Console.WriteLine(productGroup.Category);
            foreach (var prodItem in productGroup.Products)
            {
                totalItems++;
                Console.WriteLine("  {0,-10} {1}", prodItem.Name, prodItem.CategoryID);
            }
        }
        Console.WriteLine("GroupInnerJoin: {0} items in {1} named groups", totalItems, groupJoinQuery2.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void GroupJoin3()
    {

        var groupJoinQuery3 =
            from category in categories
            join product in products on category.ID equals product.CategoryID into prodGroup
            from prod in prodGroup
            orderby prod.CategoryID
            select new { Category = prod.CategoryID, ProductName = prod.Name };

        //Console.WriteLine("GroupInnerJoin:");
        int totalItems = 0;

        Console.WriteLine("GroupJoin3:");
        foreach (var item in groupJoinQuery3)
        {
            totalItems++;
            Console.WriteLine("   {0}:{1}", item.ProductName, item.Category);
        }

        Console.WriteLine("GroupJoin3: {0} items in 1 group", totalItems);
        Console.WriteLine(System.Environment.NewLine);
    }

    void LeftOuterJoin()
    {
        // Create the query.
        var leftOuterQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup.DefaultIfEmpty(new Product() { Name = "Nothing!", CategoryID = category.ID });

        // Store the count of total items (for demonstration only).
        int totalItems = 0;

        Console.WriteLine("Left Outer Join:");

        // A nested foreach statement  is required to access group items
        foreach (var prodGrouping in leftOuterQuery)
        {
            Console.WriteLine("Group:");
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("  {0,-10}{1}", item.Name, item.CategoryID);
            }
        }
        Console.WriteLine("LeftOuterJoin: {0} items in {1} groups", totalItems, leftOuterQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void LeftOuterJoin2()
    {
        // Create the query.
        var leftOuterQuery2 =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           from item in prodGroup.DefaultIfEmpty()
           select new { Name = item == null ? "Nothing!" : item.Name, CategoryID = category.ID };

        Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", leftOuterQuery2.Count());
        // Store the count of total items
        int totalItems = 0;

        Console.WriteLine("Left Outer Join 2:");

        // Groups have been flattened.
        foreach (var item in leftOuterQuery2)
        {
            totalItems++;
            Console.WriteLine("{0,-10}{1}", item.Name, item.CategoryID);
        }
        Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", totalItems);
    }
}
/*Output:

InnerJoin:
Cola      1
Tea       1
Mustard   2
Pickles   2
Carrots   3
Bok Choy  3
Peaches   5
Melons    5
InnerJoin: 8 items in 1 group.


Unshaped GroupJoin:
Group:
    Cola      1
    Tea       1
Group:
    Mustard   2
    Pickles   2
Group:
    Carrots   3
    Bok Choy  3
Group:
Group:
    Peaches   5
    Melons    5
Unshaped GroupJoin: 8 items in 5 unnamed groups


GroupInnerJoin:
Beverages
    Cola       1
    Tea        1
Condiments
    Mustard    2
    Pickles    2
Vegetables
    Bok Choy   3
    Carrots    3
Grains
Fruit
    Melons     5
    Peaches    5
GroupInnerJoin: 8 items in 5 named groups


GroupJoin3:
    Cola:1
    Tea:1
    Mustard:2
    Pickles:2
    Carrots:3
    Bok Choy:3
    Peaches:5
    Melons:5
GroupJoin3: 8 items in 1 group


Left Outer Join:
Group:
    Cola      1
    Tea       1
Group:
    Mustard   2
    Pickles   2
Group:
    Carrots   3
    Bok Choy  3
Group:
    Nothing!  4
Group:
    Peaches   5
    Melons    5
LeftOuterJoin: 9 items in 5 groups


LeftOuterJoin2: 9 items in 1 group
Left Outer Join 2:
Cola      1
Tea       1
Mustard   2
Pickles   2
Carrots   3
Bok Choy  3
Nothing!  4
Peaches   5
Melons    5
LeftOuterJoin2: 9 items in 1 group
Press any key to exit.
*/

Notes Remarks

Une clause join qui n’est pas suivie de into se traduit par l’appel de la méthode Join.A join clause that is not followed by into is translated into a Join method call. Une clause join qui n’est pas suivie de into se traduit par l’appel de la méthode GroupJoin.A join clause that is followed by into is translated to a GroupJoin method call.

Voir aussiSee also