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 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 előrejelzést mutat be egytől
int
egyigstring
. Jegyezze fel az új típusthighScoresQuery
.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 Count
lesz végrehajtva, mert Count
az eredményeken át kell iterálnia a visszaadott highScoresQuery
elemek 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 scoreQuery
keresztül. Ehelyett az iterációs változón testScore
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 mindkettő queryMajorCities
queryMajorCities2
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ő. queryCities
IEnumerable<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ű Cities
objektumgyűjteményt City
tartalmaz. Az egyes Country
objektumok 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);
}
}
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 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 country
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ó: 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 prod
a 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 Split
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ó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.