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 IEnumerableXElement<>.

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 objektu 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 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é scoreQuerydotazu . Místo toho se vrátí prostřednictvím proměnné testScoreiterace . 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é queryMajorCitiesqueryMajorCities2 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é. queryCitiesIEnumerable<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é Countryz 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) letvolitelné. 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í.

Viz také