A lekérdezési kifejezés alapjai

Ez a cikk a C#-ban található lekérdezési kifejezésekkel kapcsolatos alapfogalmakat ismerteti.

Mi az a lekérdezés, és mit tesz?

A lekérdezés utasítások készlete, amely leírja, hogy egy adott adatforrásból (vagy forrásokból) milyen adatokat kell lekérni, valamint hogy a visszaadott adatoknak milyen alakzattal és szervezettel kell rendelkezniük. A lekérdezések eltérnek az általa előállított eredményektől.

A forrásadatok általában logikusan, azonos típusú elemek sorozataként lesznek rendszerezve. Egy SQL-adatbázistábla például sorsorokat tartalmaz. Egy XML-fájlban az XML-elemek "sorozata" található (bár az XML-elemek hierarchikusan vannak rendszerezve egy faszerkezetben). A memóriában lévő gyűjtemény objektumok sorozatát tartalmazza.

Az alkalmazás szempontjából az eredeti forrásadatok konkrét típusa és szerkezete nem fontos. Az alkalmazás mindig gyűjteményként vagy IQueryable<T> gyűjteményként IEnumerable<T> látja a forrásadatokat. A LINQ-ból XML-be például a forrásadatok láthatóvá IEnumerable<XElement>lesznek.

A forrásütemezés miatt a lekérdezések a következő három dolog egyikét hajthatják végre:

  • Az elemek egy részhalmazának lekérése új sorozat létrehozásához az egyes elemek módosítása nélkül. A lekérdezés ezután különböző módokon rendezheti vagy csoportosíthatja a visszaadott sorozatot, ahogyan az a következő példában is látható (feltételezzük scores , hogy egy int[]):

    IEnumerable<int> highScoresQuery =
        from score in scores
        where score > 80
        orderby score descending
        select score;
    
  • Az előző példához hasonlóan lekérheti az elemek sorozatát, de átalakíthatja őket egy új típusú objektummá. Előfordulhat például, hogy egy lekérdezés csak az adatforrás bizonyos ügyfélrekordjaiból kéri le a családneveket. Vagy lekérheti a teljes rekordot, majd felhasználhatja egy másik memóriabeli objektumtípus vagy akár XML-adatok létrehozására a végső eredményütemezés létrehozása előtt. Az alábbi példa egy előrejelzést mutat be egytől int egyig string. Jegyezze fel az új típust highScoresQuery.

    IEnumerable<string> highScoresQuery2 =
        from score in scores
        where score > 80
        orderby score descending
        select $"The score is {score}";
    
  • A forrásadatok egy-egy adott értékének lekérése, például:

    • Egy adott feltételnek megfelelő elemek száma.

    • Az az elem, amely a legnagyobb vagy a legkisebb értékkel rendelkezik.

    • Az első elem, amely megfelel egy feltételnek, vagy egy adott elemkészlet adott értékeinek összege. Az alábbi lekérdezés például a 80-nál nagyobb pontszámok számát adja vissza az scores egész számtömbből:

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

      Az előző példában jegyezze fel a lekérdezési kifejezés zárójeleinek használatát a metódus hívása Enumerable.Count előtt. A konkrét eredmény tárolásához egy új változót is használhat.

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

Az előző példában a lekérdezés a hívásban Countlesz végrehajtva, mert Count az eredményeken át kell iterálnia a visszaadott highScoresQueryelemek számának meghatározásához.

Mi az a lekérdezési kifejezés?

A lekérdezési kifejezés egy lekérdezési szintaxisban kifejezett lekérdezés. A lekérdezési kifejezés egy első osztályú nyelvi szerkezet. Ez ugyanúgy működik, mint bármely más kifejezés, és bármely olyan környezetben használható, amelyben a C# kifejezés érvényes. A lekérdezési kifejezések az SQL-hez vagy az XQueryhez hasonló deklaratív szintaxisban írt záradékokból állnak. Minden záradék egy vagy több C#-kifejezést tartalmaz, és ezek a kifejezések maguk is lehetnek lekérdezési kifejezések vagy lekérdezési kifejezések.

A lekérdezési kifejezésnek egy from záradékkal kell kezdődnie, és egy kijelölési vagy csoportosítási záradékkal kell végződnie. Az első from és az utolsó select vagy group záradék között tartalmazhat egy vagy több választható záradékot: ahol, orderby, join, let és még egy a záradékokból. A kulcsszó használatával azt is engedélyezheti, hogy egy join vagy group több lekérdezési záradék forrásaként szolgáljon ugyanabban a lekérdezési kifejezésben.

Lekérdezési változó

A LINQ-ban a lekérdezési változók olyan változók, amelyek a lekérdezés eredményei helyett egy lekérdezést tárolnak. Pontosabban a lekérdezési változók mindig számbavételi típusok, amelyek egy utasításban vagy a metódus közvetlen hívásában foreach átszámított elemek sorozatát IEnumerator.MoveNext() állítják elő.

Feljegyzés

A cikkben szereplő példák az alábbi adatforrásokat és mintaadatokat használják.

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)])
];

Az alábbi példakód egy egyszerű lekérdezési kifejezést mutat be egy adatforrással, egy szűrési záradékkal, egy rendezési záradékkal és a forráselemek átalakítása nélkül. A select záradék befejezi a lekérdezést.

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

Az előző példában scoreQuery egy lekérdezési változót használunk, amelyet néha csak lekérdezésnek is nevezünk. A lekérdezési változó nem tárol tényleges eredményadatokat, amelyek a foreach ciklusban lesznek létrehozva. Az utasítás végrehajtásakor foreach a lekérdezési eredmények nem lesznek visszaadva a lekérdezési változón scoreQuerykeresztül. Ehelyett az iterációs változón testScorekeresztül adják vissza őket. A scoreQuery változó egy második foreach ciklusban is iterálható. Ugyanazokat az eredményeket hozza létre, amíg sem azt, sem az adatforrást nem módosították.

A lekérdezési változók olyan lekérdezést tárolhatnak, amely lekérdezési szintaxisban vagy metódusszintaxisban vagy a kettő kombinációjában van kifejezve. Az alábbi példákban mindkettő queryMajorCitiesqueryMajorCities2 lekérdezési változó:

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 > 100000
    select city;

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

// Output:
// City { Population = 120000 }
// City { Population = 112000 }
// City { Population = 150340 }

// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);

Másrészt az alábbi két példa olyan változókat mutat be, amelyek nem lekérdezési változók, annak ellenére, hogy mindegyik inicializálva van egy lekérdezéssel. Nem lekérdezési változók, mert az eredményeket tárolják:

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();

Lekérdezési változók explicit és implicit beírása

Ez a dokumentáció általában a lekérdezési változó explicit típusát biztosítja a lekérdezési változó és a kiválasztási záradék közötti típuskapcsolat megjelenítéséhez. A var kulcsszóval azonban arra is utasíthatja a fordítót, hogy fordításkor a lekérdezési változó (vagy bármely más helyi változó) típusára következtetjen. A cikkben korábban bemutatott lekérdezési példa például implicit beírással is kifejezhető:

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

Az előző példában a var használata nem kötelező. queryCitiesIEnumerable<City> implicit vagy explicit módon gépelt.

Lekérdezési kifejezés indítása

A lekérdezési kifejezésnek záradékkal from kell kezdődnie. Egy adatforrást és egy tartományváltozót határoz meg. A tartományváltozó a forrásütemezés minden egymást követő elemét jelöli a forrásütemezés bejárása közben. A tartományváltozó erősen be van állítva az adatforrás elemeinek típusa alapján. Az alábbi példában, mivel countries egy objektumtömbCountry, a tartományváltozó is be van állítva.Country Mivel a tartományváltozó erősen be van állítva, a pont operátorral elérheti a típus bármely elérhető tagját.

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

A tartományváltozó mindaddig hatókörben van, amíg a lekérdezést pontosvesszővel vagy folytatási záradékkal nem zárja ki.

Egy lekérdezési kifejezés több from záradékot is tartalmazhat. Használjon további from záradékokat, ha a forrásütemezés minden eleme maga gyűjtemény vagy gyűjteményt tartalmaz. Tegyük fel például, hogy rendelkezik egy objektumgyűjteményselCountry, amelyek mindegyike egy nevű Citiesobjektumgyűjteményt City tartalmaz. Az egyes Countryobjektumok lekérdezéséhez City használjon két from záradékot az itt látható módon:

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

További információ: záradék.

Lekérdezési kifejezés befejezése

A lekérdezési kifejezésnek záradékkal group vagy záradékkal kell végződnie select .

csoport záradéka

group A záradék használatával egy megadott kulccsal rendszerezett csoportok sorozatát hozhatja létre. A kulcs bármilyen adattípus lehet. Az alábbi lekérdezés például olyan csoportok sorozatát hozza létre, amelyek egy vagy több Country objektumot tartalmaznak, és amelynek kulcsa egy char olyan típus, amelynek értéke az országok nevének első betűje.

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

A csoportosítással kapcsolatos további információkért lásd a csoport záradékát.

select záradék

Használja a select záradékot az összes többi sorozattípus előállításához. Egy egyszerű select záradék egyszerűen ugyanolyan típusú objektumok sorozatát hozza létre, mint az adatforrásban található objektumok. Ebben a példában az adatforrás objektumokat tartalmaz Country . A orderby záradék csak egy új sorrendbe rendezi az elemeket, és a select záradék létrehozza az átrendezett objektumok sorozatát Country .

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

A select záradék segítségével a forrásadatok új típusú sorozatokká alakíthatók. Ezt az átalakítást előrejelzésnek is nevezik. Az alábbi példában a select záradék egy névtelen típusokat tartalmazó sorozatot mutat be, amely az eredeti elem mezőinek csak egy részét tartalmazza. Az új objektumok inicializálása objektum-inicializálóval történik.

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

Ebben a var példában ez azért szükséges, mert a lekérdezés névtelen típust hoz létre.

A záradék forrásadatok átalakításának minden módjáról select további információt a select záradékban talál.

Folytatások a

A kulcsszóval into egy vagy group több záradékban select létrehozhat egy ideiglenes azonosítót, amely egy lekérdezést tárol. Akkor használja a into záradékot, ha további lekérdezési műveleteket kell végrehajtania egy lekérdezésen egy csoportosítási vagy kiválasztási művelet után. Az alábbi példában countries a 10 milliós tartományban lévő populáció szerint csoportosítjuk. A csoportok létrehozása után további záradékok szűrnek ki néhány csoportot, majd növekvő sorrendbe rendezik a csoportokat. A további műveletek végrehajtásához az általa countryGroup képviselt folytatásra van szükség.

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
    from country in countries
    let percentile = (int)country.Population / 10_000_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);
    }
}

További információ:

Szűrés, rendelés és csatlakozás

A kezdő from záradék és a záró select vagy group záradék között az összes többi záradék (where, , join, orderbyfrom, let) nem kötelező. A választható záradékok bármelyike nulla vagy többször használható egy lekérdezéstörzsben.

where záradék

where A záradék használatával kiszűrheti a forrásadatok elemeit egy vagy több predikátumkifejezés alapján. Az where alábbi példában szereplő záradék egy predikátumot tartalmaz két feltétellel.

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population is < 200000 and > 100000
    select city;

További információ: hol található a záradék.

orderby záradék

orderby A záradék használatával növekvő vagy csökkenő sorrendbe rendezheti az eredményeket. Másodlagos rendezési rendeléseket is megadhat. Az alábbi példa elsődleges rendezést végez az objektumokon a countryArea tulajdonság használatával. Ezután másodlagos rendezést hajt végre a Population tulajdonság használatával.

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

A ascending kulcsszó nem kötelező; ez az alapértelmezett rendezési sorrend, ha nincs megadva sorrend. További információ: orderby záradék.

illesztési záradék

join A záradék használatával társíthatja és/vagy kombinálhatja az egyik adatforrás elemeit egy másik adatforrás elemeivel az egyes elemek megadott kulcsainak egyenlőségi összehasonlítása alapján. A LINQ-ban az illesztési műveletek különböző típusú objektumok sorozatán hajthatók végre. Két sorozat összekapcsolása után egy vagy group több select utasítással kell megadnia, hogy melyik elemet tárolja a kimeneti sorozatban. Névtelen típussal is kombinálhatja az egyes társított elemek tulajdonságait egy új típusba a kimeneti sorozathoz. Az alábbi példa olyan objektumokat prod társít, amelyek Category tulajdonsága megegyezik a categories sztringtömb egyik kategóriájával. A rendszer kiszűri azokat a termékeket, amelyek Category nem egyeznek a sztringgelcategories. Az select utasítás egy új típust jelez, amelynek tulajdonságai mind a kettőből és proda cat .

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

Csoportillesztést úgy is végrehajthat, hogy a művelet eredményeit join egy ideiglenes változóba tárolja a kulcsszó használatával . További információ: join záradék.

Let záradék

let A záradék használatával egy kifejezés eredményét, például metódushívást egy új tartományváltozóban tárolhatja. Az alábbi példában a tartományváltozó firstName a visszaadott sztringek Splittömbjének első elemét tárolja.

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

További információt a Let záradékban talál.

Lekérdezési kifejezésben szereplő albekérdezések

A lekérdezési záradékok maguk is tartalmazhatnak lekérdezési kifejezést, amelyet néha allekérdezésnek is neveznek. Minden al lekérdezés saját from záradékkal kezdődik, amely nem feltétlenül ugyanarra az adatforrásra mutat az első from záradékban. Az alábbi lekérdezés például egy lekérdezési kifejezést jelenít meg, amelyet a választó utasítás használ a csoportosítási művelet eredményeinek lekéréséhez.

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

További információ: Alkonyat végrehajtása csoportosítási műveleten.

Lásd még