Sdílet prostřednictvím


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 je int[]):

    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 do string. 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 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 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 příkazu 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 selectpromí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í.

Viz také