Bagikan melalui


Dasar-dasar ekspresi kueri

Artikel ini memperkenalkan konsep dasar yang terkait dengan ekspresi kueri di C#.

Apa itu kueri dan apa yang dilakukannya?

Kueri adalah serangkaian instruksi yang menjelaskan data apa yang harus diambil dari sumber data (atau sumber) tertentu dan bentuk dan organisasi apa yang harus dimiliki data yang dikembalikan. Kueri berbeda dengan hasil yang dihasilkannya.

Umumnya, data sumber diatur secara logis sebagai urutan elemen dari jenis yang sama. Misalnya, tabel database SQL berisi urutan baris. Dalam file XML, ada "urutan" elemen XML (meskipun elemen XML diatur secara hierarkis dalam struktur pohon). Koleksi dalam memori berisi urutan objek.

Dari sudut pandang aplikasi, jenis dan struktur tertentu dari data sumber asli tidak penting. Aplikasi selalu melihat data sumber sebagai kumpulan IEnumerable<T> atau IQueryable<T>. Misalnya, di LINQ ke XML, data sumber dibuat terlihat sebagai IEnumerable<XElement>.

Mengingat urutan sumber ini, kueri mungkin melakukan salah satu dari tiga hal:

  • Ambil subset elemen untuk menghasilkan urutan baru tanpa memodifikasi elemen individual. Kueri kemudian dapat mengurutkan atau mengelompokkan urutan yang dikembalikan dengan berbagai cara, seperti yang diperlihatkan dalam contoh berikut (asumsikan scores adalah int[]):

    IEnumerable<int> highScoresQuery =
        from score in scores
        where score > 80
        orderby score descending
        select score;
    
  • Ambil urutan elemen seperti pada contoh sebelumnya tetapi ubah ke jenis objek baru. Misalnya, kueri mungkin hanya mengambil nama keluarga dari rekaman pelanggan tertentu di sumber data. Atau mungkin mengambil rekaman lengkap lalu menggunakannya untuk membangun jenis objek dalam memori lain atau bahkan data XML sebelum menghasilkan urutan hasil akhir. Contoh berikut menunjukkan proyeksi dari int ke string. Perhatikan jenis baru highScoresQuery.

    IEnumerable<string> highScoresQuery2 =
        from score in scores
        where score > 80
        orderby score descending
        select $"The score is {score}";
    
  • Ambil nilai singleton tentang data sumber, seperti:

    • Jumlah elemen yang cocok dengan kondisi tertentu.

    • Elemen yang memiliki nilai terbesar atau paling sedikit.

    • Elemen pertama yang cocok dengan kondisi, atau jumlah nilai tertentu dalam sekumpulan elemen tertentu. Misalnya, kueri berikut mengembalikan jumlah skor yang lebih besar dari 80 dari array bilangan bulat scores:

      var highScoreCount = (
          from score in scores
          where score > 80
          select score
      ).Count();
      

      Dalam contoh sebelumnya, perhatikan penggunaan tanda kurung di sekitar ekspresi kueri sebelum panggilan ke metode Enumerable.Count. Anda juga dapat menggunakan variabel baru untuk menyimpan hasil konkret.

      IEnumerable<int> highScoresQuery3 =
          from score in scores
          where score > 80
          select score;
      
      var scoreCount = highScoresQuery3.Count();
      

Dalam contoh sebelumnya, kueri dijalankan dalam panggilan ke Count, karena Count harus melakukan iterasi atas hasil untuk menentukan jumlah elemen yang dikembalikan oleh highScoresQuery.

Apa itu ekspresi kueri?

Ekspresi kueri adalah kueri yang dinyatakan dalam sintaks kueri. Ekspresi kueri adalah konstruksi bahasa kelas satu. Ini sama seperti ekspresi lain dan dapat digunakan dalam konteks apa pun di mana ekspresi C# valid. Ekspresi kueri terdiri dari sekumpulan klausa yang ditulis dalam sintaks deklaratif yang mirip dengan SQL atau XQuery. Setiap klausa pada gilirannya berisi satu atau beberapa ekspresi C#, dan ekspresi ini mungkin berupa ekspresi kueri atau berisi ekspresi kueri.

Ekspresi kueri harus dimulai dengan klausa dari dan harus diakhiri dengan klausa pemilihan atau pengelompokan . Antara klausul from pertama dan klausul select atau group terakhir, dapat berisi satu atau beberapa klausul opsional berikut: di mana, urutkan berdasarkan, gabung, biarkan dan bahkan klausul lainnya dari. Anda juga dapat menggunakan kata kunci into untuk mengaktifkan hasil klausa join atau group untuk berfungsi sebagai sumber untuk klausa kueri lainnya dalam ekspresi kueri yang sama.

Variabel kueri

Di LINQ, variabel kueri adalah variabel apa pun yang menyimpan kueri alih-alih hasil kueri. Lebih khusus lagi, variabel kueri selalu merupakan jenis enumerable yang menghasilkan urutan elemen ketika diulang dalam pernyataan foreach atau panggilan langsung ke metode IEnumerator.MoveNext().

Nota

Contoh dalam artikel ini menggunakan sumber data dan data sampel berikut.

record City(string Name, long Population);
record Country(string Name, double Area, long Population, List<City> Cities);
record Product(string Name, string Category);
static readonly City[] cities = [
    new City("Tokyo", 37_833_000),
    new City("Delhi", 30_290_000),
    new City("Shanghai", 27_110_000),
    new City("São Paulo", 22_043_000),
    new City("Mumbai", 20_412_000),
    new City("Beijing", 20_384_000),
    new City("Cairo", 18_772_000),
    new City("Dhaka", 17_598_000),
    new City("Osaka", 19_281_000),
    new City("New York-Newark", 18_604_000),
    new City("Karachi", 16_094_000),
    new City("Chongqing", 15_872_000),
    new City("Istanbul", 15_029_000),
    new City("Buenos Aires", 15_024_000),
    new City("Kolkata", 14_850_000),
    new City("Lagos", 14_368_000),
    new City("Kinshasa", 14_342_000),
    new City("Manila", 13_923_000),
    new City("Rio de Janeiro", 13_374_000),
    new City("Tianjin", 13_215_000)
];

static readonly Country[] countries = [
    new Country ("Vatican City", 0.44, 526, [new City("Vatican City", 826)]),
    new Country ("Monaco", 2.02, 38_000, [new City("Monte Carlo", 38_000)]),
    new Country ("Nauru", 21, 10_900, [new City("Yaren", 1_100)]),
    new Country ("Tuvalu", 26, 11_600, [new City("Funafuti", 6_200)]),
    new Country ("San Marino", 61, 33_900, [new City("San Marino", 4_500)]),
    new Country ("Liechtenstein", 160, 38_000, [new City("Vaduz", 5_200)]),
    new Country ("Marshall Islands", 181, 58_000, [new City("Majuro", 28_000)]),
    new Country ("Saint Kitts & Nevis", 261, 53_000, [new City("Basseterre", 13_000)])
];

Contoh kode berikut menunjukkan ekspresi kueri sederhana dengan satu sumber data, satu klausa pemfilteran, satu klausa pengurutan, dan tidak ada transformasi elemen sumber. Klausa select mengakhiri kueri.

// Data source.
int[] scores = [90, 71, 82, 93, 75, 82];

// Query Expression.
IEnumerable<int> scoreQuery = //query variable
    from score in scores //required
    where score > 80 // optional
    orderby score descending // optional
    select score; //must end with select or group

// Execute the query to produce the results
foreach (var testScore in scoreQuery)
{
    Console.WriteLine(testScore);
}

// Output: 93 90 82 82

Dalam contoh sebelumnya, scoreQuery adalah variabel kueri , yang terkadang disebut sebagai kueri . Variabel kueri tidak menyimpan data hasil aktual, yang diproduksi dalam perulangan foreach. Dan ketika pernyataan foreach dijalankan, hasil kueri tidak dikembalikan melalui variabel kueri scoreQuery. Sebaliknya, mereka dikembalikan melalui variabel iterasi testScore. Variabel scoreQuery dapat diulang dalam perulangan foreach kedua. Ini menghasilkan hasil yang sama selama tidak ada yang dimodifikasi, baik itu maupun sumber data.

Variabel kueri mungkin menyimpan kueri yang dinyatakan dalam sintaks kueri atau sintaks metode, atau kombinasi keduanya. Dalam contoh berikut, queryMajorCities dan queryMajorCities2 adalah variabel kueri:

City[] cities = [
    new City("Tokyo", 37_833_000),
    new City("Delhi", 30_290_000),
    new City("Shanghai", 27_110_000),
    new City("São Paulo", 22_043_000)
];

//Query syntax
IEnumerable<City> queryMajorCities =
    from city in cities
    where city.Population > 30_000_000
    select city;

// Execute the query to produce the results
foreach (City city in queryMajorCities)
{
    Console.WriteLine(city);
}

// Output:
// City { Name = Tokyo, Population = 37833000 }
// City { Name = Delhi, Population = 30290000 }

// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 30_000_000);
// Execute the query to produce the results
foreach (City city in queryMajorCities2)
{
    Console.WriteLine(city);
}
// Output:
// City { Name = Tokyo, Population = 37833000 }
// City { Name = Delhi, Population = 30290000 }

Di sisi lain, dua contoh berikut menunjukkan variabel yang bukan variabel kueri meskipun masing-masing diinisialisasi dengan kueri. Mereka bukan variabel kueri karena menyimpan hasil:

var highestScore = (
    from score in scores
    select score
).Max();

// or split the expression
IEnumerable<int> scoreQuery =
    from score in scores
    select score;

var highScore = scoreQuery.Max();
// the following returns the same result
highScore = scores.Max();
var largeCitiesList = (
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city
).ToList();

// or split the expression
IEnumerable<City> largeCitiesQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;
var largeCitiesList2 = largeCitiesQuery.ToList();

Pengetikan eksplisit dan implisit variabel kueri

Dokumentasi ini biasanya menyediakan jenis eksplisit variabel kueri untuk memperlihatkan hubungan jenis antara variabel kueri dan klausa pilih. Namun, Anda juga dapat menggunakan kata kunci var untuk menginstruksikan pengkompilasi untuk menyimpulkan jenis variabel kueri (atau variabel lokal lainnya) pada waktu kompilasi. Misalnya, contoh kueri yang diperlihatkan sebelumnya dalam artikel ini juga dapat dinyatakan dengan menggunakan pengetikan implisit:

var queryCities =
    from city in cities
    where city.Population > 100000
    select city;

Dalam contoh sebelumnya, penggunaan var bersifat opsional. queryCities adalah IEnumerable<City> baik secara implisit maupun eksplisit.

Memulai ekspresi kueri

Ekspresi kueri harus dimulai dengan klausa from. Ini menentukan sumber data bersama dengan variabel rentang. Variabel rentang mewakili setiap elemen berturut-turut dalam urutan sumber saat urutan sumber sedang dilalui. Variabel rentang sangat ditik berdasarkan jenis elemen dalam sumber data. Dalam contoh berikut, karena countries adalah array objek Country, variabel rentang juga di ketikkan sebagai Country. Karena variabel rentang ditik dengan kuat, Anda dapat menggunakan operator titik untuk mengakses anggota jenis yang tersedia.

IEnumerable<Country> countryAreaQuery =
    from country in countries
    where country.Area > 20 //sq km
    select country;

Variabel rentang berada dalam cakupan hingga kueri keluar, baik dengan titik koma atau dengan klausa kelanjutan .

Ekspresi kueri mungkin berisi beberapa klausa from. Gunakan lebih banyak klausa from ketika setiap elemen dalam urutan sumber adalah koleksi itu sendiri atau berisi koleksi. Misalnya, asumsikan bahwa Anda memiliki kumpulan objek Country, yang masing-masing berisi kumpulan objek City bernama Cities. Untuk mengkueri objek City di setiap Country, gunakan dua klausa from seperti yang diperlihatkan di sini:

IEnumerable<City> cityQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;

Untuk informasi selengkapnya, lihat dari klausa.

Mengakhiri ekspresi kueri

Ekspresi kueri harus diakhir dengan klausa group atau klausa select.

Klausa kelompok

Gunakan klausa group untuk menghasilkan urutan grup yang diatur oleh kunci yang Anda tentukan. Kuncinya bisa berupa jenis data apa pun. Misalnya, kueri berikut membuat urutan grup yang berisi satu atau beberapa objek Country dan kuncinya adalah jenis char dengan nilai menjadi huruf pertama nama negara.

var queryCountryGroups =
    from country in countries
    group country by country.Name[0];

Untuk informasi selengkapnya tentang pengelompokan, lihat klausa grup .

pilih klausa

Gunakan klausa select untuk menghasilkan semua jenis urutan lainnya. Klausa select sederhana hanya menghasilkan urutan dari jenis objek yang sama dengan objek yang terkandung dalam sumber data. Dalam contoh ini, sumber data berisi objek Country. Klausa orderby hanya mengurutkan elemen ke dalam urutan baru dan klausul select menghasilkan urutan objek Country yang diurutkan ulang.

IEnumerable<Country> sortedQuery =
    from country in countries
    orderby country.Area
    select country;

Klausa select dapat digunakan untuk mengubah data sumber menjadi urutan jenis baru. Transformasi ini juga diberi nama proyeksi . Dalam contoh berikut, klausa selectmemproyeksikan urutan jenis anonim yang hanya berisi subset kolom dari elemen asal. Objek baru diinisialisasi dengan menggunakan penginisialisasi objek.

var queryNameAndPop =
    from country in countries
    select new
    {
        Name = country.Name,
        Pop = country.Population
    };

Jadi dalam contoh ini, var diperlukan karena kueri menghasilkan jenis anonim.

Untuk informasi selengkapnya tentang semua cara klausa select dapat digunakan untuk mengubah data sumber, lihat memilih klausa.

Kelanjutan dengan ke

Anda dapat menggunakan kata kunci into dalam klausa select atau group untuk membuat pengidentifikasi sementara yang menyimpan kueri. Gunakan klausa into saat Anda harus melakukan operasi kueri tambahan pada kueri setelah operasi pengelompokan atau seleksi. Dalam contoh berikut, countries dikelompokkan sesuai dengan populasi dalam kisaran 10 juta. Setelah grup ini dibuat, klausa tambahan memfilter beberapa grup, kemudian mengurutkan grup dalam urutan naik. Untuk melakukan operasi tambahan tersebut, kelanjutan yang diwakili oleh countryGroup diperlukan.

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
    from country in countries
    let percentile = (int)country.Population / 1_000
    group country by percentile into countryGroup
    where countryGroup.Key >= 20
    orderby countryGroup.Key
    select countryGroup;

// grouping is an IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
    Console.WriteLine(grouping.Key);
    foreach (var country in grouping)
    {
        Console.WriteLine(country.Name + ":" + country.Population);
    }
}

Untuk informasi selengkapnya, lihat into.

Memfilter, mengurutkan, dan bergabung

Antara klausa from awal, dan klausa select akhir atau group, semua klausa lainnya (where, join, orderby, from, let) bersifat opsional. Salah satu klausa opsional mungkin digunakan nol kali atau beberapa kali dalam isi kueri.

Klausa di mana

Gunakan klausa where untuk memfilter elemen dari data sumber berdasarkan satu atau beberapa ekspresi predikat. Klausa where dalam contoh berikut memiliki satu predikat dengan dua kondisi.

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population is < 15_000_000 and > 10_000_000
    select city;

Untuk informasi selengkapnya, lihat tempat klausa.

Klausa orderby

Gunakan klausa orderby untuk mengurutkan hasil dalam urutan naik atau turun. Anda juga dapat menentukan urutan pengurutan sekunder. Contoh berikut melakukan pengurutan utama pada objek country dengan menggunakan properti Area. Kemudian melakukan pengurutan sekunder dengan menggunakan properti Population.

IEnumerable<Country> querySortedCountries =
    from country in countries
    orderby country.Area, country.Population descending
    select country;

Kata kunci ascending bersifat opsional; ini adalah urutan pengurutan default jika tidak ada urutan yang ditentukan. Untuk informasi selengkapnya, lihat klausul urutan .

Klausa gabungan

Gunakan klausa join untuk mengaitkan dan/atau menggabungkan elemen dari satu sumber data dengan elemen dari sumber data lain berdasarkan perbandingan kesetaraan antara kunci yang ditentukan di setiap elemen. Di LINQ, operasi gabungan dilakukan pada urutan objek yang elemennya berbeda jenis. Setelah menggabungkan dua urutan, Anda harus menggunakan pernyataan select atau group untuk menentukan elemen mana yang akan disimpan dalam urutan output. Anda juga dapat menggunakan jenis anonim untuk menggabungkan properti dari setiap set elemen terkait ke dalam jenis baru untuk urutan output. Contoh berikut mengaitkan objek prod yang properti Category-nya cocok dengan salah satu kategori dalam array string categories. Produk yang Category nya tidak cocok dengan string apa pun di categories difilter. Pernyataan select memproyeksikan jenis baru yang propertinya diambil dari cat dan prod.

var categoryQuery =
    from cat in categories
    join prod in products on cat equals prod.Category
    select new
    {
        Category = cat,
        Name = prod.Name
    };

Anda juga dapat melakukan gabungan grup dengan menyimpan hasil operasi join ke dalam variabel sementara dengan menggunakan kata kunci into. Untuk informasi selengkapnya, lihat klausa gabungan .

Klausa let

Gunakan klausa let untuk menyimpan hasil ekspresi, seperti panggilan metode, dalam variabel rentang baru. Dalam contoh berikut, variabel rentang firstName menyimpan elemen pertama dari array string yang dikembalikan oleh Split.

string[] names = ["Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia"];
IEnumerable<string> queryFirstNames =
    from name in names
    let firstName = name.Split(' ')[0]
    select firstName;

foreach (var s in queryFirstNames)
{
    Console.Write(s + " ");
}

//Output: Svetlana Claire Sven Cesar

Untuk informasi selengkapnya, lihat biarkan klausa.

Subkueri dalam ekspresi kueri

Klausa kueri mungkin berisi ekspresi kueri, yang terkadang disebut sebagai subkueri . Setiap subkueri dimulai dengan klausa from sendiri yang tidak selalu menunjuk ke sumber data yang sama dalam klausa from pertama. Misalnya, kueri berikut menunjukkan ekspresi kueri yang digunakan dalam pernyataan pilih untuk mengambil hasil operasi pengelompokan.

var queryGroupMax =
    from student in students
    group student by student.Year into studentGroup
    select new
    {
        Level = studentGroup.Key,
        HighestScore = (
            from student2 in studentGroup
            select student2.ExamScores.Average()
        ).Max()
    };

Untuk informasi selengkapnya, lihat Lakukan subkueri pada operasi pengelompokan.

Lihat juga