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 pokynů, 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> nebo IQueryable<T> kolekci. Například v LINQ to XML jsou zdrojová data viditelná 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 (předpokládejme, že
scores
jeint[]
):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
int
dostring
. Poznamenejte si nový typhighScoresQuery
.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 hodnocení větších než 80 z celočíselného pole
scores
: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 metody Enumerable.Count. 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 spustí 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 obsahuje jeden nebo více výrazů jazyka C# a tyto výrazy mohou být buď výrazem dotazu, nebo obsahovat výraz dotazu.
Výraz dotazu musí začínat klauzulí a musí končit klauzulí select nebo klauzulí skupiny. Mezi první klauzulí from
a poslední select
nebo group
klauzulí může zahrnovat jednu nebo více z těchto volitelných klauzulí: kde, orderby, join, let a dokonce i další z klauzulí. Pomocí do klíčového slova můžete také povolit výsledek klauzule join
nebo group
, která bude sloužit 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á dotazu foreach
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 je scoreQuery
proměnnou dotazu , která se někdy označuje jen jako dotaz. Proměnná dotazu neuchová žádná skutečná výsledná data, která se vytvářejí ve smyčce foreach
. A když se příkaz foreach
spustí, výsledky dotazu se nevrátí prostřednictvím proměnné dotazu scoreQuery
. Místo toho se vrátí prostřednictvím iterační proměnné testScore
. Proměnnou scoreQuery
je možné itestrovat ve druhé smyčce foreach
. Vygeneruje stejné výsledky, dokud se nezměnil 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é dotazu queryMajorCities
i queryMajorCities2
:
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 }
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 ale také použít klíčové slovo var a 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
je IEnumerable<City>
s implicitním nebo explicitním typem.
Zahájení dotazovacího výrazu
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ů, proměnná rozsahu je 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 > 20 //sq km
select country;
Proměnná rozsahu zůstane v platnosti, dokud není dotaz ukončen buď středníkem, nebo pokračovací klauzulí .
Výraz dotazu může obsahovat více from
klauzulí. Použijte více klauzulí from
, když každý prvek ve zdrojové sekvenci je sám o sobě kolekcí nebo obsahuje kolekci. Předpokládejme například, že máte kolekci Country
objektů, z nichž každá obsahuje kolekci City
objektů s názvem Cities
. Pokud chcete dotazovat City
objekty v jednotlivých Country
, použijte dvě klauzule from
, 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 tématu z klauzule.
Ukončení výrazu dotazu
Výraz dotazu musí končit klauzulí group
nebo klauzulí select
.
Skupinová klauzule
Pomocí klauzule group
vytvořte posloupnost skupin uspořádaných podle zadaného klíče. Klíč může být libovolný datový typ. Následující dotaz například vytvoří sekvenci skupin, které obsahují jeden nebo více objektů Country
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 tématu skupinová klauzule.
klauzule SELECT
Pomocí klauzule select
vytvořte všechny ostatní typy sekvencí. Jednoduchá klauzule select
pouze vytvoří sekvenci stejného typu objektů jako objekty obsažené ve zdroji dat. V tomto příkladu obsahuje zdroj dat Country
objekty. Klauzule orderby
pouze seřadí prvky do nového pořadí a klauzule select
vytvoří sekvenci reordered Country
objektů.
IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
Klauzule 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 klauzule select
promítá sekvenci 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 je tedy potřeba var
, protože dotaz vytvoří anonymní typ.
Pro více informací o všech způsobech, jak lze použít klauzuli select
k transformaci zdrojových dat, se podívejte na SELECT klauzuli.
Pokračování s do
Pomocí klíčového slova into
v klauzuli select
nebo group
můžete vytvořit dočasný identifikátor, který ukládá dotaz. Použijte klauzuli into
, když je nutné po seskupování nebo výběru provést další operace s dotazem. V následujícím příkladu jsou countries
seskupené podle počtu obyvatel v rozsahu 10 milionů. Po vytvoření těchto skupin další klauzule vyfiltrují některé skupiny a poté se skupiny seřadí 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 / 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);
}
}
Další informace najdete v tématu do.
Filtrování, řazení a spojování
Mezi počáteční from
klauzulí a koncovou select
nebo klauzulí group
jsou volitelné všechny ostatní klauzule (where
, join
, orderby
, from
, let
). Libovolná volitelná klauzule se může v textu dotazu používat nulou nebo vícekrát.
Podmínka WHERE
Pomocí klauzule where
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 < 15_000_000 and > 10_000_000
select city;
Další informace naleznete v tématu , kde je klauzule.
Klauzule ORDERBY
Pomocí klauzule orderby
seřaďte výsledky vzestupně nebo sestupně. Můžete také zadat sekundární pořadí řazení. Následující příklad provádí primární řazení na objekty country
pomocí vlastnosti Area
. Potom provede sekundární řazení pomocí vlastnosti Population
.
IEnumerable<Country> querySortedCountries =
from country in countries
orderby country.Area, country.Population descending
select country;
Klíčové slovo ascending
je volitelné; je to výchozí pořadí řazení, pokud není zadáno žádné pořadí. Další informace viz klauzuli orderby.
Klauzule spojení
Klauzule join
slouží k přidružení a/nebo kombinování prvků 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 příkaz select
nebo group
k určení prvku, který má být uložen 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 poli řetězce categories
. Produkty, jejichž Category
neodpovídají žádnému řetězci v categories
, se odfiltrují. Příkaz select
projektuje nový typ, jehož vlastnosti jsou převzaty z cat
i 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ů operace join
do dočasné proměnné pomocí do klíčového slova. Další informace viz příkaz join.
Klauzule let
Pomocí klauzule let
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á rozsahu firstName
ukládá první prvek vráceného pole řetězců 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 tématu let klauzule.
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í klauzulí from
, která nemusí nutně odkazovat na stejný zdroj dat v první klauzuli from
. 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 části Proveďte poddotaz na operaci seskupení.