Bagikan melalui


klausa gabungan (Referensi C#)

Klausa join berguna untuk mengaitkan elemen dari urutan sumber yang berbeda yang tidak memiliki hubungan langsung dalam model objek. Satu-satunya persyaratan adalah bahwa elemen di setiap sumber berbagi beberapa nilai yang dapat dibandingkan untuk kesetaraan. Misalnya, distributor makanan mungkin memiliki daftar pemasok produk tertentu, dan daftar pembeli. Klausa join dapat digunakan, misalnya, untuk membuat daftar pemasok dan pembeli produk tersebut yang semuanya berada di wilayah yang ditentukan yang sama.

Klausa join mengambil dua urutan sumber sebagai input. Elemen dalam setiap urutan harus berupa atau berisi properti yang dapat dibandingkan dengan properti yang sesuai dalam urutan lain. Klausa join membandingkan kunci yang ditentukan untuk kesetaraan dengan menggunakan kata kunci equals khusus. Semua gabungan yang dilakukan oleh klausa join adalah gabungan ekuivalen (equijoins). Bentuk output klausa join tergantung pada jenis gabungan tertentu yang Anda lakukan. Berikut ini adalah tiga jenis gabungan yang paling umum:

  • Gabungan Dalam

  • Bergabung dengan grup

  • Gabungan kiri luar

Gabungan Dalam

Contoh berikut menunjukkan equijoin internal yang sederhana. Kueri ini menghasilkan urutan datar pasangan "nama/kategori produk". String kategori yang sama akan muncul di beberapa elemen. Jika elemen dari categories tidak memiliki productsyang cocok , kategori tersebut tidak akan muncul dalam hasil.

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

Untuk informasi selengkapnya, lihat Melakukan inner join.

Bergabung dengan grup

Klausa join dengan ekspresi into disebut penggabungan grup.

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

Gabungan grup menghasilkan urutan hasil hierarkis, yang mengaitkan elemen dalam urutan sumber kiri dengan satu atau beberapa elemen yang cocok dalam urutan sumber sisi kanan. Penggabungan grup tidak memiliki padanan dalam istilah relasional; ini pada dasarnya adalah rangkaian array objek.

Jika tidak ada elemen dari urutan sumber yang tepat yang ditemukan untuk mencocokkan elemen di sumber kiri, klausa join akan menghasilkan array kosong untuk item tersebut. Oleh karena itu, gabungan grup pada dasarnya masih merupakan inner-equijoin kecuali bahwa urutan hasil diatur ke dalam grup.

Jika Anda hanya memilih hasil dari penggabungan grup, Anda dapat mengakses item, tetapi Anda tidak dapat mengidentifikasi kunci yang sesuai. Oleh karena itu, umumnya lebih berguna untuk memilih hasil gabungan grup ke dalam jenis baru yang juga memiliki nama kunci, seperti yang ditunjukkan pada contoh sebelumnya.

Anda juga dapat, tentu saja, menggunakan hasil gabungan grup sebagai generator subkueri lain:

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;

Untuk informasi selengkapnya, lihat Lakukan gabungan terkelompok.

Gabungan kiri luar

Dalam gabungan luar kiri, semua elemen dalam urutan sumber kiri dikembalikan, bahkan jika tidak ada elemen yang cocok dalam urutan kanan. Untuk melakukan gabungan luar kiri di LINQ, gunakan metode DefaultIfEmpty dalam kombinasi dengan gabungan grup untuk menentukan elemen sisi kanan default yang akan dihasilkan jika elemen sisi kiri tidak memiliki kecocokan. Anda dapat menggunakan null sebagai nilai default untuk jenis referensi apa pun, atau Anda dapat menentukan jenis default yang ditentukan pengguna. Dalam contoh berikut, jenis default yang ditentukan pengguna ditampilkan:

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

Untuk informasi selengkapnya, lihat Lakukan left outer join.

Operator sama dengan

Klausa join melakukan sambungan kesetaraan. Dengan kata lain, Anda hanya dapat mencocokkan berdasarkan kesetaraan dua kunci. Jenis perbandingan lain seperti "lebih besar dari" atau "tidak sama" tidak didukung. Untuk memperjelas bahwa semua gabungan merupakan equijoin, klausa join menggunakan kata kunci equals sebagai pengganti operator ==. Kata kunci equals hanya dapat digunakan dalam klausa join dan berbeda dari operator == dengan beberapa cara penting. Saat membandingkan string, equals memiliki kelebihan beban untuk dibandingkan dengan nilai dan operator == menggunakan kesetaraan referensi. Ketika kedua sisi perbandingan memiliki variabel string yang identik, equals dan == mencapai hasil yang sama: true. Itu karena, ketika program mendeklarasikan dua atau lebih variabel string yang setara, pengkompilasi menyimpan semuanya di lokasi yang sama. Ini dikenal sebagai interning. Perbedaan penting lainnya adalah perbandingan null: null equals null dievaluasi sebagai false dengan operator equals, bukan operator == yang mengevaluasinya sebagai true. Terakhir, perilaku cakupan berbeda: dengan equals, kunci kiri menggunakan urutan sumber luar, dan kunci kanan menggunakan sumber dalam. Sumber daya luar hanya berada dalam cakupan di sisi kiri equals dan urutan sumber daya internal hanya dalam cakupan di sisi kanan.

Penggabungan non-ekuivalen (Non-equijoins)

Anda dapat melakukan non-equijoin, gabungan silang, dan operasi gabungan kustom lainnya dengan memakai beberapa klausa from untuk secara mandiri memasukkan urutan baru ke dalam kueri. Untuk informasi selengkapnya, lihat Lakukan operasi gabungan kustom.

Penggabungan pada koleksi objek vs. tabel relasional

Dalam ekspresi kueri LINQ, operasi gabungan dilakukan pada koleksi objek. Koleksi objek tidak dapat "digabungkan" dengan cara yang sama persis dengan dua tabel relasional. Di LINQ, klausa join eksplisit hanya diperlukan ketika dua urutan sumber tidak terikat oleh hubungan apa pun. Saat bekerja dengan LINQ ke SQL, tabel kunci asing diwakili dalam model objek sebagai properti tabel utama. Misalnya, dalam database Northwind, tabel Pelanggan memiliki hubungan kunci asing dengan tabel Pesanan. Saat Anda memetakan tabel ke model objek, kelas Pelanggan memiliki properti Pesanan yang berisi kumpulan Pesanan yang terkait dengan Pelanggan tersebut. Pada dasarnya, penggabungan telah dilakukan untuk Anda.

Untuk informasi selengkapnya tentang mengkueri di seluruh tabel terkait dalam konteks LINQ ke SQL, lihat Cara: Memetakan Hubungan Database.

Kunci komposit

Anda dapat menguji kesetaraan beberapa nilai dengan menggunakan kunci komposit. Untuk informasi selengkapnya, lihat Bergabung dengan menggunakan kunci komposit. Kunci komposit juga dapat digunakan dalam klausa group.

Contoh

Contoh berikut membandingkan hasil inner join, group join, dan left outer join pada sumber data yang sama dengan menggunakan kunci yang sama. Beberapa kode tambahan ditambahkan ke contoh ini untuk mengklarifikasi hasil di tampilan konsol.

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: {innerJoinQuery.Count()} items in 1 group.");
        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: {totalItems} items in {groupJoinQuery.Count()} unnamed groups");
        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: {totalItems} items in {groupJoinQuery2.Count()} named groups");
        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($"   {item.ProductName}:{item.Category}");
        }

        Console.WriteLine($"GroupJoin3: {totalItems} items in 1 group");
        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: {totalItems} items in {leftOuterQuery.Count()} groups");
        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: {leftOuterQuery2.Count()} items in 1 group");
        // 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: {totalItems} items in 1 group");
    }
}
/*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.
*/

Komentar

Klausa join yang tidak diikuti oleh into diterjemahkan ke dalam panggilan metode Join. Klausa join yang diikuti oleh into diterjemahkan ke panggilan metode GroupJoin.

Lihat juga