join – klauzule (Referenční dokumentace jazyka C#)

joinKlauzule je užitečná pro přidružení prvků z různých zdrojových sekvencí, které nemají přímý vztah v objektovém modelu. Jediným požadavkem je, aby elementy v jednotlivých zdrojích sdílely určitou hodnotu, která může být porovnána s rovností. Například distributor potravinářského prostředí může mít seznam dodavatelů určitého produktu a seznam nákupčích. joinKlauzuli lze použít například k vytvoření seznamu dodavatelů a nákupčích tohoto produktu, kteří jsou ve stejné konkrétní oblasti.

joinKlauzule přijímá jako vstup dvě zdrojové sekvence. Prvky v každé sekvenci musí být buď nebo obsahují vlastnost, která může být porovnána s odpovídající vlastností v druhé sekvenci. joinKlauzule porovnává zadané klíče k rovnosti pomocí equals klíčového slova Special. Všechna spojení prováděná join klauzulí jsou equijoins. Tvar výstupu join klauzule závisí na konkrétním typu spojení, které provádíte. Níže jsou tři nejběžnější typy spojení:

  • Vnitřní spojení

  • Spojení skupin

  • Levé vnější spojení

Vnitřní spojení

Následující příklad ukazuje jednoduchý vnitřní equijoin. Tento dotaz vytvoří nestrukturovanou posloupnost párů "název produktu/kategorie". Stejný řetězec kategorie se zobrazí ve více prvcích. Pokud prvek z categories nemá žádnou shodu products , tato kategorie se ve výsledcích nezobrazí.

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

Další informace najdete v tématu provádění vnitřních spojení.

Spojení skupin

joinKlauzule s into výrazem se nazývá spojení se skupinou.

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

Spojení se skupinou vytvoří hierarchickou sekvenci výsledků, která přidruží prvky v levé zdrojové sekvenci k jednomu nebo více shodným prvkům zdrojové sekvence na pravé straně. Spojení se skupinami nemá v relačních výrazech žádný ekvivalent. v podstatě je to sekvence polí objektů.

Pokud se nenajde žádné elementy z pravé zdrojové sekvence, aby odpovídaly elementu v levém zdroji, klauzule vytvoří join pro tuto položku prázdné pole. Proto je spojení se skupinou stále v podstatě vnitřní equijoin s tím rozdílem, že je sekvence výsledků uspořádána do skupin.

Pokud jste vybrali jenom výsledky spojení se skupinami, můžete k položkám přistupovat, ale nemůžete identifikovat klíč, na kterém se shodují. Proto je obecně vhodnější vybrat výsledky připojení skupiny do nového typu, který má také název klíče, jak je znázorněno v předchozím příkladu.

Můžete samozřejmě také použít výsledek spojení skupiny jako generátor jiného poddotazu:

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;

Další informace najdete v tématu provedení seskupených spojení.

Levé vnější spojení

V levém vnějším spojení jsou vráceny všechny prvky v levé zdrojové sekvenci, a to i v případě, že v pravé sekvenci nejsou žádné vyhovující prvky. K provedení levého vnějšího spojení v LINQ použijte DefaultIfEmpty metodu v kombinaci s připojením skupiny k určení výchozího prvku na pravé straně, který se vytvoří, pokud element na levé straně nemá žádné shody. Můžete použít null jako výchozí hodnotu libovolného typu odkazu nebo můžete zadat uživatelsky definovaný výchozí typ. V následujícím příkladu je zobrazen uživatelsky definovaný výchozí typ:

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

Další informace najdete v tématu provedení levých vnějších spojení.

Operátor Equals

joinKlauzule provede výjimku equijoin. Jinými slovy, můžete pouze základní shody s rovností dvou klíčů. Jiné typy porovnání, jako je "větší než" nebo "nerovnost", nejsou podporovány. Aby bylo jasné, že se všechna spojení equijoins, join použije klauzule equals místo operátoru klíčové slovo == . equalsKlíčové slovo lze použít pouze v join klauzuli a == v některých důležitých způsobech se liší od operátoru. Při porovnávání řetězců equals má přetížení k porovnání podle hodnoty a operátor == používá rovnost odkazů. Pokud obě strany porovnání mají stejné řetězcové proměnné equals a == dosáhnou stejného výsledku: true. To je způsobeno tím, že když program deklaruje dvě nebo více ekvivalentních řetězcových proměnných, kompilátor je uloží do stejného umístění, viz referenční rovnost a interning pro řetězce pro další informace. Dalším důležitým rozdílem je porovnání hodnoty null: null equals null je vyhodnocen jako false s equals operátorem namísto == operátoru, který ho vyhodnocuje jako true. Nakonec se chování oboru liší: v equals , levý klíč spotřebuje vnější zdrojovou sekvenci a pravý klíč spotřebuje vnitřní zdroj. Vnější zdroj je pouze v rozsahu na levé straně equals a vnitřní zdrojová sekvence je pouze v rozsahu na pravé straně.

Bez equijoins

fromK zavedení nových sekvencí nezávisle na dotaz můžete použít více klauzulí, a to pomocí více klauzulí. Další informace najdete v tématu provádění vlastních operací spojení.

Spojení s kolekcemi objektů vs. relačními tabulkami

Ve výrazu dotazu LINQ se operace join provádí na kolekcích objektů. Kolekce objektů nemohou být "připojené" stejným způsobem jako dvě relační tabulky. V LINQ jsou explicitní join klauzule požadovány pouze v případě, že dvě zdrojové sekvence nejsou svázány s žádnou relací. Při práci s se Technologie LINQ to SQL tabulky cizích klíčů v objektovém modelu reprezentují jako vlastnosti primární tabulky. Například v databázi Northwind má tabulka Customer relaci cizího klíče s tabulkou Orders. Při mapování tabulek na objektový model má třída Customer vlastnost Orders, která obsahuje kolekci objednávek přidružených k danému zákazníkovi. V důsledku toho se připojení už pro vás udělalo.

Další informace o dotazování napříč souvisejícími tabulkami v kontextu Technologie LINQ to SQL naleznete v tématu How to: map Database Relationships.

Složené klíče

K otestování rovnosti více hodnot můžete použít složený klíč. Další informace najdete v tématu připojení pomocí složených klíčů. V klauzuli lze použít také složené klíče group .

Příklad

Následující příklad porovnává výsledky vnitřního spojení, spojení skupin a levé vnější spojení se stejnými zdroji dat pomocí stejných shodných klíčů. Do těchto příkladů se přidá nějaký další kód, který vyjasní výsledky v zobrazení konzoly.

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.
*/

Poznámky

joinKlauzule, která není následována, into je přeložena do Join volání metody. joinKlauzule, která je následována, into je přeložena na GroupJoin volání metody.

Viz také