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ések utasítások készlete, amelyek ismertetik, hogy egy adott adatforrásból (vagy forrásokból) milyen adatokat kell lekérni, és milyen alakzattal és szervezettel kell rendelkezniük a visszaadott adatoknak. 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 IEnumerable<T> vagy IQueryable<T> gyűjteményként látja a forrásadatokat. A LINQ to XML esetében például a forrásadatok láthatók IEnumerable
<XElement>.
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 az alábbi példában látható módon (tegyük fel, hogy
scores
egyint[]
):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
int
és egystring
közötti előrejelzést mutatja be. Figyelje meg ahighScoresQuery
új típusát.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
scores
egész számtömb 80-nál nagyobb pontszámait adja vissza:var highScoreCount = ( from score in scores where score > 80 select score ).Count();
Az előző példában jegyezze fel a zárójelek használatát a lekérdezési kifejezés körül a Enumerable.Count metódus hívása 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ést a Count
hívása hajtja végre, mert Count
az eredményeket iterálnia kell a highScoresQuery
által visszaadott elemek számának meghatározásához.
Mi az a lekérdezési kifejezés?
A lekérdezési kifejezés olyan lekérdezés, amely a lekérdezésszintaxist használja. 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 tartalmazhatnak lekérdezési kifejezéseket.
A lekérdezési kifejezésnek záradékkal kell kezdődnie, és vagy csoport záradékkal kell végződnie. Az első from
záradék és az utolsó select
vagy group
záradék között tartalmazhat egy vagy több választható záradékot: ahol, rendelés szerint, összekapcsolás, legyen, sőt egy másik záradékot is. A into
kulcsszóval is engedélyezheti egy join
vagy group
záradék eredményét, hogy forrásként szolgáljon több lekérdezési záradékhoz 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 lekérdezési tárolnak a lekérdezés eredményei helyett. Pontosabban a lekérdezésváltozók mindig számbavehető típusok, amelyek egy foreach
utasítás vagy a IEnumerator.MoveNext() metódus közvetlen hívása során több elemsort állítanak elő.
Jegyzet
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 a scoreQuery
egy lekérdezési változó, amelyet néha csak lekérdezésinéven is neveznek. A lekérdezési változó nem tárol tényleges eredményadatokat, amelyek a foreach
ciklusban lesznek létrehozva. A foreach
utasítás végrehajtásakor a lekérdezési eredmények nem lesznek visszaadva a scoreQuery
lekérdezési változón keresztül. Ehelyett a testScore
iterációs változón keresztü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 queryMajorCities
és queryMajorCities2
is lekérdezési változók:
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 }
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 adja meg a lekérdezési változó és a választó záradékközötti típuskapcsolat megjelenítéséhez. A var kulcsszóval azonban arra is utasíthatja a fordítót, hogy a 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ő.
queryCities
egy IEnumerable<City>
, akár implicit módon, akár explicit módon van begépelve.
Lekérdezési kifejezés indítása
A lekérdezési kifejezésnek egy from
záradékkal 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 a countries
a Country
objektumok tömbje, a tartományváltozó típusa is 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 > 20 //sq km
select country;
A tartományváltozó hatókörben marad, amíg a lekérdezést vagy pontosvesszővel, vagy folytatási záradékkal le nem zárják.
Egy lekérdezési kifejezés több from
záradékot is tartalmazhat. Használjon több from
záradékot, ha a forrásütemezés minden eleme maga gyűjtemény vagy gyűjteményt tartalmaz. Tegyük fel például, hogy Country
objektumgyűjteménye van, amelyek mindegyike City
Cities
nevű objektumgyűjteményt tartalmaz. Az egyes City
Country
objektumainak lekérdezéséhez 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óért lásd a részt azáradékból.
Lekérdezési kifejezés befejezése
A lekérdezési kifejezésnek group
záradékkal vagy select
záradékkal kell végződnie.
A csoport záradéka
A group
záradék használatával egy megadott kulccsal rendszerezett csoportok sorozatát hozhatja létre. A kulcs bármilyen adattípus lehet. A következő lekérdezés például egy vagy több Country
objektumot tartalmazó csoportok sorozatát hozza létre, amelyek kulcsa egy char
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];
További információért a csoportosításról tekintse meg a csoportos záradékot.
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ú objektumsorozatot hoz létre, mint az adatforrásban található objektumok. Ebben a példában az adatforrás Country
objektumokat tartalmaz. A orderby
záradék csak egy új sorrendbe rendezi az elemeket, és a select
záradék létrehozza az átrendezett Country
objektumok sorozatát.
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ípusok sorozatává alakíthatók. Ezt az átalakítást vetítésnekis nevezik. A következő példában a select
záradék projektek névtelen típusok sorozatát, amelyek csak az eredeti elem mezőinek egy részét tartalmazzák. 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 példában a var
azért van szükség, mert a lekérdezés névtelen típust hoz létre.
További információt az select
záradék forrásadatok átalakításának minden módjáról a válogató záradékválasztásával kapcsolatban találhat.
Folytatások -ból-be
A into
kulcsszót egy select
vagy group
záradékban használhatja egy lekérdezést tároló ideiglenes azonosító létrehozásához. A into
záradékot akkor használja, ha további lekérdezési műveleteket kell végrehajtania egy lekérdezésen a csoportosítási vagy kiválasztási művelet után. Az alábbi példában a countries
10 milliós tartományban lévő populáció szerint vannak csoportosítva. 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 a countryGroup
által 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 / 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);
}
}
További információ: into
.
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
, orderby
, from
, let
) nem kötelező. A választható záradékok bármelyike nulla vagy többször is használható egy lekérdezés törzsében.
A WHERE záradék
A where
záradék használatával kiszűrheti a forrásadatok elemeit egy vagy több predikátumkifejezés alapján. Az alábbi példában szereplő where
záradék egy predikátumot tartalmaz két feltétellel.
IEnumerable<City> queryCityPop =
from city in cities
where city.Population is < 15_000_000 and > 10_000_000
select city;
További információért lásd a(z) , ahol megtalálható a(z) záradék.
Az orderby záradék
A orderby
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 egy elsődleges rendezést hajt végre a country
objektumokon a Area
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óért lásd: rendezési záradék.
Az illesztési záradék
A join
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 select
vagy group
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 prod
objektumokat társít, amelyek Category
tulajdonsága megegyezik a categories
sztringtömb egyik kategóriájával. Azokat a termékeket, amelyek Category
nem felelnek meg a categories
bármely sztringjének, a rendszer kiszűri. A select
utasítás egy új típust hoz létre, amelynek tulajdonságai mind a cat
-ből, mind a prod
-ből származnak.
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 join
művelet eredményeit egy ideiglenes változóba tárolja a into
kulcsszó használatával. További információért lásd a(z) illesztési záradékot.
A let záradék
A let
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 firstName
tartományváltozó a Split
által visszaadott sztringek tö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óért lásd a let kifejezést.
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 al-lekérdezésnéven is hívnak. 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óért lásd: Al-lekérdezés végrehajtása egy csoportosítási műveleten.