Základy výrazů dotazů
Tento článek představuje základní koncepty související s výrazy dotazů v jazyce C#.
Co je dotaz a co to dělá?
Dotaz je sada instrukcí, které popisují, jaká data se mají načíst z daného zdroje dat (nebo zdrojů) a jaký tvar a organizace by vrácená data měla mít. Dotaz se liší od výsledků, které vytvoří.
Zdrojová data jsou obecně uspořádaná logicky jako posloupnost prvků stejného druhu. Například tabulka databáze SQL obsahuje posloupnost řádků. V souboru XML existuje "posloupnost" elementů XML (i když jsou elementy XML uspořádané hierarchicky ve stromové struktuře). Kolekce v paměti obsahuje posloupnost objektů.
Z pohledu aplikace není důležitý konkrétní typ a struktura původních zdrojových dat. Aplikace vždy vidí zdrojová data jako IEnumerable<T> kolekci nebo jako IQueryable<T> kolekci. Například v JAZYCE LINQ to XML se zdrojová data zviditelní jako IEnumerable
XElement<>.
Vzhledem k této sekvenci zdroje může dotaz provést jednu ze tří věcí:
Načtěte podmnožinu prvků, aby se vytvořila nová sekvence beze změny jednotlivých prvků. Dotaz pak může řadit nebo seskupovat vrácenou sekvenci různými způsoby, jak je znázorněno v následujícím příkladu
int[]
(předpokládejme,scores
že ):IEnumerable<int> highScoresQuery = from score in scores where score > 80 orderby score descending select score;
Načte posloupnost prvků jako v předchozím příkladu, ale transformuje je na nový typ objektu. Dotaz může například načíst pouze jména rodiny z určitých záznamů zákazníků ve zdroji dat. Nebo může načíst úplný záznam a pak ho použít k vytvoření jiného typu objektu v paměti nebo dokonce dat XML před vygenerováním konečné sekvence výsledků. Následující příklad ukazuje projekci z objektu do
int
objektustring
. Poznamenejte si nový typ .highScoresQuery
IEnumerable<string> highScoresQuery2 = from score in scores where score > 80 orderby score descending select $"The score is {score}";
Načtěte hodnotu singletonu o zdrojových datech, například:
Počet prvků, které odpovídají určité podmínce.
Prvek, který má nejvyšší nebo nejnižší hodnotu.
První prvek, který odpovídá podmínce, nebo součet konkrétních hodnot v zadané sadě prvků. Například následující dotaz vrátí počet skóre větší než 80 z celočíselného
scores
pole:var highScoreCount = ( from score in scores where score > 80 select score ).Count();
V předchozím příkladu si všimněte použití závorek kolem výrazu dotazu před voláním Enumerable.Count metody. K uložení konkrétního výsledku můžete použít také novou proměnnou.
IEnumerable<int> highScoresQuery3 = from score in scores where score > 80 select score; var scoreCount = highScoresQuery3.Count();
V předchozím příkladu se dotaz provede ve volání Count
, protože Count
musí iterovat výsledky, aby bylo možné určit počet prvků vrácených highScoresQuery
.
Co je výraz dotazu?
Výraz dotazu je dotaz vyjádřený v syntaxi dotazu. Výraz dotazu je prvotřídní jazyková konstrukce. Je to stejně jako jakýkoli jiný výraz a dá se použít v libovolném kontextu, ve kterém je výraz jazyka C# platný. Výraz dotazu se skládá ze sady klauzulí napsaných v deklarativní syntaxi podobné jazyku SQL nebo XQuery. Každá klauzule zase obsahuje jeden nebo více výrazů jazyka C# a tyto výrazy můžou být buď výrazem dotazu, nebo výrazem dotazu.
Výraz dotazu musí začínat klauzulí from a musí končit klauzulí select nebo group . Mezi první from
klauzulí a poslední select
nebo group
klauzulí může obsahovat jednu nebo více z těchto volitelných klauzulí: where, orderby, join, let a dokonce i jiné z klauzulí. Pomocí klíčového join
slova into můžete také povolit, aby výsledek klauzule nebo group
klauzule sloužil jako zdroj pro více klauzulí dotazu ve stejném výrazu dotazu.
Proměnná dotazu
V LINQ je proměnná dotazu libovolná proměnná, která ukládá dotaz místo výsledků dotazu. Konkrétně je proměnná dotazu vždy výčtovým typem, který vytváří posloupnost prvků při iterování v foreach
příkazu nebo přímém volání metody IEnumerator.MoveNext() .
Poznámka:
Příklady v tomto článku používají následující zdroj dat a ukázková data.
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)])
];
Následující příklad kódu ukazuje jednoduchý výraz dotazu s jedním zdrojem dat, jednou klauzulí filtrování, jednou klauzulí řazení a žádnou transformací zdrojových prvků. Klauzule select
ukončí dotaz.
// 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
V předchozím příkladu scoreQuery
je proměnná dotazu, která se někdy označuje jako jen dotaz. Proměnná dotazu neuchová žádná skutečná výsledná data, která se vytvářejí ve foreach
smyčce. A když se foreach
příkaz spustí, výsledky dotazu se nevrátí prostřednictvím proměnné scoreQuery
dotazu . Místo toho se vrátí prostřednictvím proměnné testScore
iterace . Proměnnou scoreQuery
lze itestrovat ve druhé foreach
smyčce. Výsledkem jsou stejné výsledky, dokud se nezmění ani zdroj dat.
Proměnná dotazu může obsahovat dotaz vyjádřený syntaxí dotazu nebo syntaxí metody nebo kombinací těchto dvou. V následujících příkladech jsou proměnné queryMajorCities
queryMajorCities2
dotazu:
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);
Na druhou stranu následující dva příklady ukazují proměnné, které nejsou proměnnými dotazu, i když jsou inicializovány pomocí dotazu. Nejsou to proměnné dotazu, protože ukládají výsledky:
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();
Explicitní a implicitní zadávání proměnných dotazu
Tato dokumentace obvykle poskytuje explicitní typ proměnné dotazu, aby bylo možné zobrazit vztah typu mezi proměnnou dotazu a klauzulí select. Můžete však také pomocí klíčového slova var dát kompilátoru pokyn, aby odvodil typ proměnné dotazu (nebo jakékoli jiné místní proměnné) v době kompilace. Příklad dotazu, který byl zobrazen dříve v tomto článku, se dá vyjádřit také pomocí implicitního psaní:
var queryCities =
from city in cities
where city.Population > 100000
select city;
V předchozím příkladu je použití var volitelné. queryCities
IEnumerable<City>
je implicitně nebo explicitně napsaný.
Spuštění výrazu dotazu
Výraz dotazu musí začínat klauzulí from
. Určuje zdroj dat společně s proměnnou rozsahu. Proměnná rozsahu představuje každý po sobě jdoucí prvek ve zdrojové sekvenci při procházení zdrojové sekvence. Proměnná rozsahu je silně typována na základě typu prvků ve zdroji dat. V následujícím příkladu, protože countries
je pole Country
objektů, je proměnná rozsahu také zadána jako Country
. Vzhledem k tomu, že proměnná rozsahu je silného typu, můžete pomocí operátoru tečky získat přístup ke všem dostupným členům typu.
IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > 500000 //sq km
select country;
Proměnná rozsahu je v oboru, dokud se dotaz ukončí středníkem nebo klauzulí pro pokračování .
Výraz dotazu může obsahovat více from
klauzulí. from
Další klauzule použijte, když každý prvek ve zdrojové sekvenci je sám kolekce nebo obsahuje kolekci. Předpokládejme například, že máte kolekci Country
objektů, z nichž každá obsahuje kolekci City
objektů pojmenovaných Cities
. K dotazování City
na objekty v každé Country
z nich použijte dvě from
klauzule, jak je znázorněno zde:
IEnumerable<City> cityQuery =
from country in countries
from city in country.Cities
where city.Population > 10000
select city;
Další informace najdete v klauzuli z klauzule.
Ukončení výrazu dotazu
Výraz dotazu musí končit group
klauzulí nebo klauzulí select
.
group – klauzule
Klauzule group
slouží k vytvoření posloupnosti skupin uspořádaných podle vámi zadaného klíče. Klíč může být libovolný datový typ. Následující dotaz například vytvoří posloupnost skupin, které obsahují jeden nebo více Country
objektů a jejichž klíč je char
typ s hodnotou, která je prvním písmenem názvů zemí.
var queryCountryGroups =
from country in countries
group country by country.Name[0];
Další informace o seskupování najdete v klauzuli group.
select – klauzule (C#)
select
Pomocí klauzule můžete vytvořit všechny ostatní typy sekvencí. Jednoduchá select
klauzule pouze vytvoří sekvenci stejného typu objektů jako objekty obsažené ve zdroji dat. V tomto příkladu obsahuje zdroj Country
dat objekty. Klauzule orderby
pouze seřadí prvky do nového pořadí a select
klauzule vytvoří sekvenci přeuspořádaných Country
objektů.
IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
Klauzuli select
lze použít k transformaci zdrojových dat na posloupnosti nových typů. Tato transformace se také nazývá projekce. V následujícím příkladu select
klauzule projektuje posloupnost anonymních typů, která obsahuje pouze podmnožinu polí v původním prvku. Nové objekty jsou inicializovány pomocí inicializátoru objektů.
var queryNameAndPop =
from country in countries
select new
{
Name = country.Name,
Pop = country.Population
};
V tomto příkladu var
je tedy nutné, protože dotaz vytvoří anonymní typ.
Další informace o všech způsobech použití select
klauzule k transformaci zdrojových dat naleznete v tématu select klauzule.
Pokračování s
Klíčové slovo v select
klauzuli nebo group
klíčové slovo můžete použít into
k vytvoření dočasného identifikátoru, který ukládá dotaz. Klauzuli into
použijte, když je nutné provést další operace dotazu po seskupování nebo výběru operace. V následujícím příkladu countries
jsou seskupené podle počtu obyvatel v rozsahu 10 milionů. Po vytvoření těchto skupin vyfiltrují některé skupiny další klauzule a potom seřadí skupiny vzestupně. K provedení těchto dodatečných operací se vyžaduje pokračování reprezentované countryGroup
.
// 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);
}
}
Další informace najdete v tématu.
Filtrování, řazení a spojování
Mezi počáteční from
klauzulí a koncovou select
nebo group
klauzulí jsou všechny ostatní klauzule (where
, join
, orderby
, from
) let
volitelné. Libovolná volitelná klauzule se může v textu dotazu používat nulou nebo vícekrát.
where – klauzule
where
Pomocí klauzule vyfiltrujte prvky ze zdrojových dat na základě jednoho nebo více predikátových výrazů. Klauzule where
v následujícím příkladu má jeden predikát se dvěma podmínkami.
IEnumerable<City> queryCityPop =
from city in cities
where city.Population is < 200000 and > 100000
select city;
Další informace najdete v klauzuli where.
orderby – klauzule
orderby
Pomocí klauzule můžete výsledky seřadit vzestupně nebo sestupně. Můžete také zadat sekundární pořadí řazení. Následující příklad provádí primární řazení objektů country
pomocí Area
vlastnosti. Potom pomocí vlastnosti provede sekundární řazení Population
.
IEnumerable<Country> querySortedCountries =
from country in countries
orderby country.Area, country.Population descending
select country;
Klíčové ascending
slovo je volitelné. Jedná se o výchozí pořadí řazení, pokud není zadáno žádné pořadí. Další informace naleznete v klauzuli orderby.
join – klauzule
join
Pomocí klauzule můžete přidružit a/nebo zkombinovat prvky z jednoho zdroje dat k prvkům z jiného zdroje dat na základě porovnání rovnosti mezi zadanými klíči v každém prvku. V LINQ se operace spojení provádějí na sekvencích objektů, jejichž prvky jsou různé typy. Po spojení dvou sekvencí je nutné použít select
příkaz nebo group
určit, který prvek se má uložit ve výstupní sekvenci. Anonymní typ můžete také použít ke kombinování vlastností z každé sady přidružených prvků do nového typu pro výstupní sekvenci. Následující příklad přidruží prod
objekty, jejichž Category
vlastnost odpovídá jedné z kategorií v řetězcovém categories
poli. Produkty, jejichž Category
neodpovídají žádnému řetězci, categories
se odfiltrují. Příkaz select
projektuje nový typ, jehož vlastnosti jsou převzaty z obou cat
a prod
.
var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
select new
{
Category = cat,
Name = prod.Name
};
Spojení skupiny můžete provést také uložením výsledků join
operace do dočasné proměnné pomocí klíčového slova into . Další informace najdete v tématu klauzule join.
let – klauzule
let
Pomocí klauzule uložte výsledek výrazu, například volání metody, do nové proměnné rozsahu. V následujícím příkladu proměnná firstName
rozsahu ukládá první prvek pole řetězců vrácených 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
Další informace najdete v klauzuli let.
Poddotazy ve výrazu dotazu
Klauzule dotazu může sama obsahovat výraz dotazu, který se někdy označuje jako poddotaz. Každý poddotaz začíná vlastní from
klauzulí, která nemusí nutně odkazovat na stejný zdroj dat v první from
klauzuli. Následující dotaz například ukazuje výraz dotazu, který se používá v příkazu select k načtení výsledků operace seskupení.
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()
};
Další informace najdete v tématu Provedení poddotazu operace seskupení.