Klauzula join (odwołanie w C#)join clause (C# Reference)

Klauzula join jest przydatna do kojarzenia elementów z różnych sekwencji źródłowych, które nie mają bezpośredniej relacji w modelu obiektu.The join clause is useful for associating elements from different source sequences that have no direct relationship in the object model. Jedynym wymaganiem jest to, że elementy w każdym źródle mają pewną wartość, którą można porównać dla równości.The only requirement is that the elements in each source share some value that can be compared for equality. Na przykład dystrybutor żywności może mieć listę dostawców określonego produktu i listę kupujących.For example, a food distributor might have a list of suppliers of a certain product, and a list of buyers. Klauzula join może służyć, na przykład, aby utworzyć listę dostawców i nabywców tego produktu, którzy są w tym samym określonym regionie.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.

Klauzula join przyjmuje dwie sekwencje źródłowe jako dane wejściowe.A join clause takes two source sequences as input. Elementy w każdej sekwencji musi być lub zawierać właściwość, która może być porównywana z odpowiednią właściwość w innej sekwencji.The elements in each sequence must either be or contain a property that can be compared to a corresponding property in the other sequence. Klauzula join porównuje określone klucze równości przy equals użyciu specjalnego słowa kluczowego.The join clause compares the specified keys for equality by using the special equals keyword. Wszystkie sprzężenia wykonywane join przez klauzulę są równoważne.All joins performed by the join clause are equijoins. Kształt danych wyjściowych join klauzuli zależy od określonego typu sprzężenia, które wykonujesz.The shape of the output of a join clause depends on the specific type of join you are performing. Oto trzy najczęściej spotykane typy sprzężenia:The following are three most common join types:

  • Sprzężenie wewnętrzneInner join

  • Dołączanie do grupyGroup join

  • Lewe sprzężenie zewnętrzneLeft outer join

Sprzężenie wewnętrzneInner join

W poniższym przykładzie przedstawiono prosty wewnętrzny przyłączyć.The following example shows a simple inner equijoin. To zapytanie tworzy płaską sekwencję par "nazwa produktu / kategoria".This query produces a flat sequence of "product name / category" pairs. Ten sam ciąg kategorii pojawi się w wielu elementach.The same category string will appear in multiple elements. Jeśli element categories z nie productsma dopasowania , tej kategorii nie pojawi się w wynikach.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

Aby uzyskać więcej informacji, zobacz Wykonywanie sprzężeń wewnętrznych.For more information, see Perform inner joins.

Dołączanie do grupyGroup join

Klauzula join z into wyrażeniem jest nazywany sprzężenia grupy.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 };

Sprzężenie grupy tworzy hierarchiczną sekwencję wyników, która kojarzy elementy w lewej sekwencji źródłowej z co najmniej jednym pasującym elementem w prawej sekwencji źródłowej po prawej stronie.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. Dołączanie do grupy nie ma odpowiednika w kategoriach relacyjnych; jest to zasadniczo sekwencja tablic obiektów.A group join has no equivalent in relational terms; it is essentially a sequence of object arrays.

Jeśli żadne elementy z prawej sekwencji źródłowej nie zostaną join znalezione tak, aby pasowały do elementu w lewym źródle, klauzula spowoduje wygenerowanie pustej tablicy dla tego elementu.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. W związku z tym sprzężenia grupy jest nadal w zasadzie wewnętrznej równołączyć z tą różnicą, że sekwencja wyników jest zorganizowana w grupy.Therefore, the group join is still basically an inner-equijoin except that the result sequence is organized into groups.

Jeśli po prostu wybierzesz wyniki sprzężenia grupy, możesz uzyskać dostęp do elementów, ale nie możesz zidentyfikować klucza, który pasują do nich.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. W związku z tym jest ogólnie bardziej przydatne, aby wybrać wyniki połączenia grupy do nowego typu, który ma również nazwę klucza, jak pokazano w poprzednim przykładzie.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.

Można również, oczywiście, użyć wyniku sprzężenia grupy jako generator innej podkwerendy: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;

Aby uzyskać więcej informacji, zobacz Wykonywanie sprzężeń grupowanych.For more information, see Perform grouped joins.

Lewe sprzężenie zewnętrzneLeft outer join

W lewym sprzężeniu zewnętrznym wszystkie elementy w lewej sekwencji źródłowej są zwracane, nawet jeśli żadne pasujące elementy znajdują się w odpowiedniej kolejności.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. Aby wykonać sprzężenie zewnętrzne lewe DefaultIfEmpty w LINQ, należy użyć metody w połączeniu z sprzężenia grupy, aby określić domyślny element po prawej stronie do produkcji, jeśli element po lewej stronie nie ma dopasowania.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. Jako wartość null domyślną można użyć jako wartości domyślnej dla dowolnego typu odwołania lub można określić typ domyślny zdefiniowany przez użytkownika.You can use null as the default value for any reference type, or you can specify a user-defined default type. W poniższym przykładzie wyświetlany jest typ domyślny zdefiniowany przez użytkownika: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 };

Aby uzyskać więcej informacji, zobacz Wykonywanie sprzężeń zewnętrznych .For more information, see Perform left outer joins.

Operator równych sobieThe equals operator

Klauzula join wykonuje przyłączyć.A join clause performs an equijoin. Innymi słowy można tylko oprzeć dopasowania na równości dwóch kluczy.In other words, you can only base matches on the equality of two keys. Inne rodzaje porównań, takie jak "większe niż" lub "nie równa się" nie są obsługiwane.Other types of comparisons such as "greater than" or "not equals" are not supported. Aby wyjaśnić, że wszystkie sprzężenia join są równorzędne, klauzula używa słowa kluczowego equals zamiast == operatora.To make clear that all joins are equijoins, the join clause uses the equals keyword instead of the == operator. Słowo equals kluczowe może być join używane tylko w klauzuli i różni się od == operatora w jeden ważny sposób.The equals keyword can only be used in a join clause and it differs from the == operator in one important way. Z equals, lewy klucz zużywa sekwencję źródła zewnętrznego, a prawy klucz zużywa źródło wewnętrzne.With equals, the left key consumes the outer source sequence, and the right key consumes the inner source. Zewnętrzne źródło znajduje się tylko w equals zakresie po lewej stronie, a sekwencja źródła wewnętrznego znajduje się tylko w zakresie po prawej stronie.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.

NierównowatkiNon-equijoins

Można wykonywać non-joinjoins, sprzężenia krzyżowego i innych from operacji sprzężenia niestandardowego przy użyciu wielu klauzul do wprowadzania nowych sekwencji niezależnie do kwerendy.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. Aby uzyskać więcej informacji, zobacz Wykonywanie operacji sprzężenia niestandardowego.For more information, see Perform custom join operations.

Sprzężenia w kolekcjach obiektów a tabele relacyjneJoins on object collections vs. relational tables

W wyrażeniu kwerendy LINQ operacje sprzężenia są wykonywane w kolekcjach obiektów.In a LINQ query expression, join operations are performed on object collections. Kolekcje obiektów nie mogą być "połączone" w dokładnie taki sam sposób, jak dwie tabele relacyjne.Object collections cannot be "joined" in exactly the same way as two relational tables. W LINQ join jawne klauzule są wymagane tylko wtedy, gdy dwie sekwencje źródłowe nie są powiązane przez żadną relację.In LINQ, explicit join clauses are only required when two source sequences are not tied by any relationship. Podczas pracy LINQ to SQLLINQ to SQLz , tabele kluczy obcych są reprezentowane w modelu obiektu jako właściwości tabeli podstawowej.When working with LINQ to SQLLINQ to SQL, foreign key tables are represented in the object model as properties of the primary table. Na przykład w bazie danych Northwind tabela Customer ma relację klucza obcego z tabelą Zamówienia.For example, in the Northwind database, the Customer table has a foreign key relationship with the Orders table. Podczas mapowania tabel do modelu obiektu, Customer klasy ma Orders właściwość, która zawiera kolekcję zamówień skojarzonych z tym klientem.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. W efekcie sprzężenie zostało już wykonane dla Ciebie.In effect, the join has already been done for you.

Aby uzyskać więcej informacji na temat wykonywania zapytań w powiązanych tabelach LINQ to SQLLINQ to SQLw kontekście , zobacz Jak: Mapowanie relacji bazy danych.For more information about querying across related tables in the context of LINQ to SQLLINQ to SQL, see How to: Map Database Relationships.

Klawisze kompozytoweComposite keys

Można przetestować pod kątem równości wielu wartości przy użyciu klucza złożonego.You can test for equality of multiple values by using a composite key. Aby uzyskać więcej informacji, zobacz Dołączanie przy użyciu kluczy złożonych.For more information, see Join by using composite keys. Klucze złożone mogą być group również używane w klauzuli.Composite keys can be also used in a group clause.

PrzykładExample

W poniższym przykładzie porównano wyniki sprzężenia wewnętrznego, sprzężenia grupy i sprzężenia zewnętrznego w tym samym źródle danych przy użyciu tych samych pasujących kluczy.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. Niektóre dodatkowe kodu jest dodawany do tych przykładów, aby wyjaśnić wyniki na wyświetlaczu konsoli.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.
*/

UwagiRemarks

Klauzula, join która nie into jest następnie Join jest tłumaczone na wywołanie metody.A join clause that is not followed by into is translated into a Join method call. Klauzula, join która into następuje jest GroupJoin tłumaczone na wywołanie metody.A join clause that is followed by into is translated to a GroupJoin method call.

Zobacz teżSee also