Share via


join záradék (C# referencia)

A join záradék olyan különböző forrásütemezésekből származó elemek társítására használható, amelyek nem rendelkeznek közvetlen kapcsolatokkal az objektummodellben. Az egyetlen követelmény az, hogy az egyes forráselemeknek legyen egy olyan értéke, amely összehasonlítható az egyenlőség szempontjából. Egy élelmiszer-forgalmazó például rendelkezhet egy bizonyos termék szállítóinak listájával és a vásárlók listájával. Egy join záradékot használhat például a termék szállítóinak és vevőinek listájának létrehozására, akik mind ugyanabban a meghatározott régióban találhatók.

A join záradék bemenetként két forrásütemezést használ. Az egyes sorozatok elemeinek vagy olyan tulajdonságot kell tartalmazniuk, amely összehasonlítható a másik sorozat megfelelő tulajdonságával. A join záradék a speciális equals kulcsszó használatával hasonlítja össze az egyenlőséghez megadott kulcsokat. A záradék által join végrehajtott összes illesztés egyenértékű. A záradék kimenetének join alakja a végrehajtott illesztés típusától függ. A következő három leggyakoribb illesztéstípus:

  • Belső illesztés

  • Csoporthoz való csatlakozás

  • Bal oldali külső illesztés

Belső illesztés

Az alábbi példában egy egyszerű belső egyenrangúság látható. Ez a lekérdezés a "terméknév/ kategória" párok egybesimított sorozatát hozza létre. Ugyanaz a kategóriasztring több elemben is megjelenik. Ha egy elemnek categories nincs egyezője products, az a kategória nem jelenik meg az eredmények között.

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

További információ: Belső illesztések végrehajtása.

Csoporthoz való csatlakozás

Egy join kifejezéssel into rendelkező záradékot csoportillesztésnek nevezünk.

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

A csoportillesztés hierarchikus eredményütemezést hoz létre, amely a bal oldali forrásütemezés elemeit társítja a jobb oldali forrásütemezés egy vagy több egyező eleméhez. A csoportillesztés relációs értelemben nem egyenértékű; lényegében objektumtömbök sorozata.

Ha a jobb forrásütemezés egyik eleme sem felel meg a bal oldali forrás egyik elemének, a join záradék egy üres tömböt hoz létre az adott elemhez. Ezért a csoportillesztés alapvetően még mindig belső egyenrangú, azzal a kivételével, hogy az eredményütemezés csoportokba van rendezve.

Ha csak egy csoportillesztés eredményeit választja ki, hozzáférhet az elemekhez, de nem tudja azonosítani azokat a kulcsokat, amelyeken megegyeznek. Ezért általában hasznosabb, ha a csoportillesztés eredményeit egy olyan új típusba választja ki, amely szintén tartalmazza a kulcsnevet, ahogy az előző példában is látható.

A csoportillesztés eredményét természetesen használhatja egy másik alquery generátoraként is:

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;

További információ: Csoportosított illesztések végrehajtása.

Bal oldali külső illesztés

Bal oldali külső illesztés esetén a rendszer a bal oldali forrásütemezés összes elemét visszaadja, még akkor is, ha a megfelelő elemek nem szerepelnek a megfelelő sorrendben. Ha bal oldali külső illesztést szeretne végrehajtani a LINQ-ban, a metódus és a DefaultIfEmpty csoportillesztés együttes használatával adjon meg egy alapértelmezett jobb oldali elemet, amely akkor állítható elő, ha egy bal oldali elem nem rendelkezik egyezéssel. Bármilyen referenciatípus alapértelmezett értékeként használható null , vagy megadhat egy felhasználó által definiált alapértelmezett típust. Az alábbi példában egy felhasználó által definiált alapértelmezett típus jelenik meg:

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

További információ: Bal oldali külső illesztések végrehajtása.

Az egyenlő operátor

A join záradék ekvivalens műveletet hajt végre. Más szóval csak a két kulcs egyenlőségére alapozhatja a találatokat. Más típusú összehasonlítások, például a "nagyobb mint" vagy a "nem egyenlő" nem támogatottak. Annak érdekében, hogy az összes illesztés egyenrangú legyen, a záradék a join kulcsszót equals használja az == operátor helyett. A equals kulcsszó csak záradékban join használható, és néhány fontos módon eltér az == operátortól. A sztringek equals összehasonlítása során túlterhelt az érték, és az operátor == hivatkozási egyenlőséget használ. Ha az összehasonlítás mindkét oldala azonos sztringváltozókkal rendelkezik, equals és == ugyanazt az eredményt éri el: igaz. Ennek az az oka, hogy amikor egy program két vagy több egyenértékű sztringváltozót deklarál, a fordító mindet ugyanazon a helyen tárolja. Ezt internálásnak nevezzük. Egy másik fontos különbség a null összehasonlítás: null equals null a függvény az operátorral equals hamisként értékeli ki a függvényt, és nem == igazként értékeli ki. Végül a hatókörkezelési viselkedés eltérő: a equalsbal oldali kulcs a külső forrásütemezést használja, a jobb oldali kulcs pedig a belső forrást. A külső forrás csak a bal oldalon equals található hatókörben van, a belső forrásütemezés pedig csak a jobb oldalon található hatókörben.

Nem egyenrangú

A nem egyenlő illesztéseket, keresztcsatlakozásokat és más egyéni illesztési műveleteket több from záradék használatával is végrehajthatja, hogy egymástól függetlenül új sorozatokat vezessen be egy lekérdezésbe. További információ: Egyéni illesztési műveletek végrehajtása.

Illesztések objektumgyűjteményeken és relációs táblákon

EGY LINQ-lekérdezési kifejezésben az illesztési műveletek objektumgyűjteményeken lesznek végrehajtva. Az objektumgyűjtemények nem "csatlakoztathatók" pontosan ugyanúgy, mint két relációs tábla. A LINQ-ban csak akkor van szükség explicit join záradékokra, ha két forrásütemezést nem köt semmilyen kapcsolat. A LINQ és az SQL használatakor az idegen kulcstáblák az objektummodellben az elsődleges tábla tulajdonságaiként jelennek meg. A Northwind-adatbázisban például az Ügyfél tábla idegenkulcs-kapcsolattal rendelkezik a Rendelések táblával. Amikor a táblákat az objektummodellhez rendeli, a Customer osztály egy Rendelések tulajdonsággal rendelkezik, amely az adott ügyfélhez társított rendelések gyűjteményét tartalmazza. Az illesztés gyakorlatilag már megtörtént.

A LINQ és az SQL kontextusában lévő kapcsolódó táblák lekérdezéséről további információt az Adatbázis-kapcsolatok leképezése című témakörben talál.

Összetett kulcsok

Összetett kulccsal több érték egyenlőségét is tesztelheti. További információ: Csatlakozás összetett kulcsokkal. Az összetett kulcsok záradékokban group is használhatók.

Példa

Az alábbi példa egy belső illesztés, egy csoportillesztés és egy bal oldali külső illesztés eredményeit hasonlítja össze ugyanazon az adatforráson ugyanazokkal az egyező kulcsokkal. Ezekhez a példákhoz hozzáadunk néhány további kódot, hogy egyértelműsítsük az eredményeket a konzolon.

class JoinDemonstration
{
    #region Data

    class Product
    {
        public required string Name { get; init; }
        public required int CategoryID { get; init; }
    }

    class Category
    {
        public required string Name { get; init; }
        public required int ID { get; init; }
    }

    // Specify the first data source.
    List<Category> categories =
    [
        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 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();
    }

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

Megjegyzések

A join nem követett into záradék metódushívássá Join lesz lefordítva. A join következő into záradékot a metódushívásra GroupJoin fordítjuk le.

Lásd még