klausul gabung (Referensi C#)

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

Klausul join mengambil dua urutan sumber sebagai masukan. Elemen-elemen dalam setiap barisan harus berupa atau mengandung suatu sifat yang dapat dibandingkan dengan suatu sifat yang bersesuaian dalam urutan lainnya. Klausul join membandingkan kunci yang ditentukan untuk kesetaraan dengan menggunakan kata kunci equals khusus. Semua gabungan yang dilakukan oleh klausul join adalah equijoins. Bentuk output klausul join tergantung pada jenis gabungan tertentu yang Anda lakukan. Berikut ini adalah tiga jenis gabungan yang paling umum:

  • Gabungan Dalam

  • Gabungan grup

  • Gabungan luar kiri

Gabungan Dalam

Contoh berikut menunjukkan equijoin dalam 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 pencocokan products, 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 gabungan dalam.

Gabungan grup

Klausul join dengan ekspresi into disebut gabungan 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 di urutan sumber kiri dengan satu atau lebih elemen yang cocok di urutan sumber sisi kanan. Gabung grup tidak memiliki padanan dalam hal relasional; itu pada dasarnya adalah urutan array objek.

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

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

Anda juga tentu saja dapat menggunakan hasil gabungan grup sebagai penghasil 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 Melakukan gabungan grup.

Gabungan luar kiri

Dalam gabungan kiri luar, semua elemen dalam urutan sumber kiri dikembalikan, bahkan jika tidak ada elemen yang cocok dalam urutan yang tepat. Untuk melakukan gabungan kiri luar di LINQ, gunakan metode DefaultIfEmpty dalam kombinasi dengan gabungan grup untuk menentukan elemen sisi kanan default yang akan dihasilkan jika elemen sisi kiri tidak cocok. Anda dapat menggunakan null sebagai nilai default untuk semua jenis referensi, atau Anda dapat menentukan jenis default yang ditentukan pengguna. Dalam contoh berikut, jenis default yang ditentukan pengguna akan 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 Melakukan gabungan kiri luar.

Operator yang sama

Klausul join melakukan equijoin. Dengan kata lain, Anda hanya dapat mendasarkan kecocokan pada kesetaraan dua kunci. Jenis perbandingan lain seperti "lebih besar dari" atau "tidak sama" tidak akan didukung. Untuk memperjelas bahwa semua gabungan setara, klausul join menggunakan kata kunci equals alih-alih operator ==. Kata kunci equals hanya dapat digunakan dalam klausul join, dan berbeda dari operator == dengan beberapa cara penting. Saat membandingkan string, equals mengalami 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 yang tepat menggunakan sumber dalam. Sumber luar hanya dalam cakupan di sisi kiri equals dan urutan sumber dalam hanya dalam cakupan di sisi kanan.

Non-equijoins

Anda dapat melakukan non-equijoins, cross join, dan operasi gabungan kustom lainnya dengan menggunakan beberapa klausul from untuk memperkenalkan urutan baru secara independen ke dalam kueri. Untuk informasi selengkapnya, lihat Melakukan operasi gabungan kustom.

Gabungan pada koleksi objek vs. tabel relasional

Dalam ekspresi kueri LINQ, operasi gabungan dilakukan pada kumpulan objek. Kumpulan objek tidak dapat "digabungkan" dengan cara yang sama persis seperti dua tabel relasional. Dalam LINQ, klausul join eksplisit hanya diperlukan ketika dua urutan sumber tidak terikat oleh hubungan apa pun. Saat bekerja dengan LINQ hingga SQL, tabel kunci asing direpresentasikan 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 berisi kumpulan Pesanan terkait dengan Pelanggan tersebut. Akibatnya, gabungan sudah dilakukan untuk Anda.

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

Kunci komposit

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

Contoh

Contoh berikut membandingkan hasil gabungan dalam, gabungan grup, dan gabungan kiri luar pada sumber data yang sama dengan menggunakan kunci pencocokan yang sama. Beberapa kode tambahan ditambahkan ke contoh ini untuk memperjelas 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: {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.
*/

Keterangan

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

Lihat juga