Podstawowe informacje o wyrażeniach zapytań

W tym artykule przedstawiono podstawowe pojęcia związane z wyrażeniami zapytań w języku C#.

Co to jest zapytanie i co robi?

Zapytanie to zestaw instrukcji opisujących dane do pobrania z danego źródła danych (lub źródeł) oraz kształt i organizację zwracanych danych. Zapytanie różni się od wyników, które generuje.

Ogólnie rzecz biorąc, dane źródłowe są zorganizowane logicznie jako sekwencja elementów tego samego rodzaju. Na przykład tabela bazy danych SQL zawiera sekwencję wierszy. W pliku XML istnieje "sekwencja" elementów XML (chociaż elementy XML są zorganizowane hierarchicznie w strukturze drzewa). Kolekcja w pamięci zawiera sekwencję obiektów.

Z punktu widzenia aplikacji konkretny typ i struktura oryginalnych danych źródłowych nie są ważne. Aplikacja zawsze widzi dane źródłowe jako IEnumerable<T> kolekcję lub IQueryable<T> . Na przykład w linQ to XML dane źródłowe są widoczne jako IEnumerableXElement<>.

Biorąc pod uwagę tę sekwencję źródłową, zapytanie może wykonać jedną z trzech czynności:

  • Pobierz podzestaw elementów, aby utworzyć nową sekwencję bez modyfikowania poszczególnych elementów. Zapytanie może następnie sortować lub grupować zwróconą sekwencję na różne sposoby, jak pokazano w poniższym przykładzie (załóżmy scores , że jest to int[]):

    IEnumerable<int> highScoresQuery =
        from score in scores
        where score > 80
        orderby score descending
        select score;
    
  • Pobierz sekwencję elementów, tak jak w poprzednim przykładzie, ale przekształć je w nowy typ obiektu. Na przykład zapytanie może pobierać tylko nazwy rodzin z niektórych rekordów klientów w źródle danych. Może też pobrać pełny rekord, a następnie użyć go do konstruowania innego typu obiektu w pamięci, a nawet danych XML przed wygenerowaniem sekwencji wyników końcowych. W poniższym przykładzie przedstawiono projekcję z zakresu od do int .string Zanotuj nowy typ elementu highScoresQuery.

    IEnumerable<string> highScoresQuery2 =
        from score in scores
        where score > 80
        orderby score descending
        select $"The score is {score}";
    
  • Pobierz pojedynczą wartość danych źródłowych, na przykład:

    • Liczba elementów pasujących do określonego warunku.

    • Element, który ma największą lub najmniejszą wartość.

    • Pierwszy element zgodny z warunkiem lub suma określonych wartości w określonym zestawie elementów. Na przykład następujące zapytanie zwraca liczbę wyników większych niż 80 z tablicy całkowitej scores :

      var highScoreCount = (
          from score in scores
          where score > 80
          select score
      ).Count();
      

      W poprzednim przykładzie zwróć uwagę na użycie nawiasów wokół wyrażenia zapytania przed wywołaniem Enumerable.Count metody . Możesz również użyć nowej zmiennej do przechowywania konkretnego wyniku.

      IEnumerable<int> highScoresQuery3 =
          from score in scores
          where score > 80
          select score;
      
      var scoreCount = highScoresQuery3.Count();
      

W poprzednim przykładzie zapytanie jest wykonywane w wywołaniu metody Count, ponieważ Count musi iterować wyniki w celu określenia liczby elementów zwracanych przez highScoresQueryelement .

Co to jest wyrażenie zapytania?

Wyrażenie zapytania jest zapytaniem wyrażonym w składni zapytania. Wyrażenie zapytania jest konstrukcją języka pierwszej klasy. Jest to tak samo jak każde inne wyrażenie i może być używane w dowolnym kontekście, w którym wyrażenie języka C# jest prawidłowe. Wyrażenie zapytania składa się z zestawu klauzul napisanych w składni deklaratywnej podobnej do języka SQL lub XQuery. Każda klauzula z kolei zawiera co najmniej jedno wyrażenie języka C#, a te wyrażenia mogą być wyrażeniem zapytania lub zawierać wyrażenie zapytania.

Wyrażenie zapytania musi zaczynać się od klauzuli from i musi kończyć się klauzulą select lub group . Między pierwszą from klauzulą a ostatnią select klauzulą lub group może zawierać co najmniej jedną z tych klauzul opcjonalnych: gdzie, orderby, join, let, a nawet inne klauzule z klauzul. Możesz również użyć słowa kluczowego into , aby umożliwić wynik join klauzuli lub group , aby służyć jako źródło większej liczby klauzul zapytania w tym samym wyrażeniu zapytania.

Zmienna kwerendy

W linQ zmienna kwerendy jest dowolną zmienną, która przechowuje zapytanie zamiast wyników zapytania. W szczególności zmienna kwerendy jest zawsze typem wyliczalnym, który tworzy sekwencję elementów w przypadku iteracji w foreach instrukcji lub bezpośrednim wywołaniu metody IEnumerator.MoveNext() .

Uwaga

Przykłady w tym artykule używają następującego źródła danych i przykładowych danych.

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)])
];

Poniższy przykład kodu przedstawia proste wyrażenie zapytania z jednym źródłem danych, jedną klauzulą filtrowania, jedną klauzulą porządkowania i bez przekształcenia elementów źródłowych. Klauzula select kończy zapytanie.

// 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

W poprzednim przykładzie scoreQuery jest zmienną kwerendy, która czasami jest nazywana tylko zapytaniem. Zmienna kwerendy nie przechowuje rzeczywistych danych wynikowych, które są generowane w foreach pętli. A po wykonaniu foreach instrukcji wyniki zapytania nie są zwracane za pośrednictwem zmiennej scoreQueryzapytania . Zamiast tego są one zwracane za pośrednictwem zmiennej testScoreiteracji . Zmienna scoreQuery może być iteracja w drugiej foreach pętli. Generuje on te same wyniki, o ile ani nie został zmodyfikowany, ani źródło danych.

Zmienna kwerendy może przechowywać zapytanie wyrażone w składni zapytania lub składni metody albo kombinację tych dwóch. W poniższych przykładach zmienne queryMajorCities zapytania i queryMajorCities2 są to:

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);

Z drugiej strony w poniższych dwóch przykładach pokazano zmienne, które nie są zmiennymi zapytania, mimo że każda z nich jest inicjowana za pomocą zapytania. Nie są to zmienne zapytań, ponieważ przechowują wyniki:

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();

Jawne i niejawne wpisywanie zmiennych zapytania

Ta dokumentacja zwykle udostępnia jawny typ zmiennej zapytania, aby pokazać relację typu między zmienną kwerendy a klauzulą select. Można jednak również użyć słowa kluczowego var w celu poinstruowania kompilatora, aby wywnioskował typ zmiennej zapytania (lub dowolną inną zmienną lokalną) w czasie kompilacji. Na przykład przykład zapytania, który został przedstawiony wcześniej w tym artykule, można również wyrazić przy użyciu niejawnego pisania:

var queryCities =
    from city in cities
    where city.Population > 100000
    select city;

W poprzednim przykładzie użycie wariancja jest opcjonalne. queryCities jest jawnie IEnumerable<City> lub jawnie wpisany.

Uruchamianie wyrażenia zapytania

Wyrażenie zapytania musi zaczynać się od klauzuli from . Określa źródło danych wraz ze zmienną zakresu. Zmienna zakresu reprezentuje każdy kolejny element w sekwencji źródłowej, ponieważ sekwencja źródłowa jest przechodzina. Zmienna zakresu jest silnie typizowana na podstawie typu elementów w źródle danych. W poniższym przykładzie, ponieważ countries jest tablicą Country obiektów, zmienna zakresu jest również wpisywana jako Country. Ponieważ zmienna zakresu jest silnie typizowana, możesz użyć operatora kropki, aby uzyskać dostęp do wszystkich dostępnych elementów członkowskich typu.

IEnumerable<Country> countryAreaQuery =
    from country in countries
    where country.Area > 500000 //sq km
    select country;

Zmienna zakresu jest w zakresie, dopóki kwerenda nie zostanie zakończona średnikiem lub klauzulą kontynuacji .

Wyrażenie zapytania może zawierać wiele from klauzul. Użyj większej liczby from klauzul, gdy każdy element w sekwencji źródłowej jest kolekcją lub zawiera kolekcję. Załóżmy na przykład, że masz kolekcję Country obiektów, z których każda zawiera kolekcję City obiektów o nazwie Cities. Aby wysłać zapytanie do City obiektów w każdym Countryobiekcie , użyj dwóch from klauzul, jak pokazano poniżej:

IEnumerable<City> cityQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;

Aby uzyskać więcej informacji, zobacz klauzulę from.

Kończenie wyrażenia zapytania

Wyrażenie zapytania musi kończyć się klauzulą group lub klauzulą select .

group — Klauzula

Użyj klauzuli group , aby utworzyć sekwencję grup zorganizowanych według określonego klucza. Klucz może być dowolnym typem danych. Na przykład poniższe zapytanie tworzy sekwencję grup zawierających co najmniej jeden Country obiekt i którego klucz jest typem z wartością będącą pierwszą literą char nazw krajów.

var queryCountryGroups =
    from country in countries
    group country by country.Name[0];

Aby uzyskać więcej informacji na temat grupowania, zobacz klauzulę group.

select — Klauzula

Użyj klauzuli select , aby utworzyć wszystkie inne typy sekwencji. Prosta select klauzula tworzy sekwencję obiektów tego samego typu co obiekty znajdujące się w źródle danych. W tym przykładzie źródło danych zawiera Country obiekty. Klauzula orderby po prostu sortuje elementy w nową kolejność, a select klauzula tworzy sekwencję ponownie uporządkowanych Country obiektów.

IEnumerable<Country> sortedQuery =
    from country in countries
    orderby country.Area
    select country;

Klauzula select może służyć do przekształcania danych źródłowych w sekwencje nowych typów. Ta transformacja nosi również nazwę projekcji. W poniższym przykładzie klauzula selectprojektuje sekwencję typów anonimowych, która zawiera tylko podzestaw pól w oryginalnym elemecie. Nowe obiekty są inicjowane przy użyciu inicjatora obiektów.

var queryNameAndPop =
    from country in countries
    select new
    {
        Name = country.Name,
        Pop = country.Population
    };

W tym przykładzie parametr jest wymagany, var ponieważ zapytanie generuje typ anonimowy.

Aby uzyskać więcej informacji o wszystkich sposobach użycia klauzuli do przekształcania danych źródłowych, zobacz klauzulęselect select.

Kontynuacje z

Słowa kluczowego into w klauzuli select or group można użyć do utworzenia tymczasowego identyfikatora, który przechowuje zapytanie. Użyj klauzuli into , jeśli musisz wykonywać dodatkowe operacje zapytań na zapytaniu po operacji grupowania lub wybierania. W poniższym przykładzie countries są grupowane według populacji w zakresie 10 milionów. Po utworzeniu tych grup więcej klauzul filtruje niektóre grupy, a następnie sortuje grupy w kolejności rosnącej. Aby wykonać te dodatkowe operacje, wymagana jest kontynuacja reprezentowana przez countryGroup program .

// 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);
    }
}

Aby uzyskać więcej informacji, zobacz.

Filtrowanie, porządkowanie i łączenie

Między klauzulą początkową from a końcową select lub group klauzulą wszystkie inne klauzule (where, join, orderby, from, let) są opcjonalne. Każda z klauzul opcjonalnych może być używana zero razy lub wiele razy w treści zapytania.

Klauzula where

Użyj klauzuli where , aby odfiltrować elementy z danych źródłowych na podstawie co najmniej jednego wyrażenia predykatu. Klauzula where w poniższym przykładzie ma jeden predykat z dwoma warunkami.

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population is < 200000 and > 100000
    select city;

Aby uzyskać więcej informacji, zobacz klauzulę where.

Klauzula orderby

Użyj klauzuli orderby , aby posortować wyniki w kolejności rosnącej lub malejącej. Można również określić pomocnicze zamówienia sortowania. Poniższy przykład wykonuje sortowanie podstawowe na country obiektach przy użyciu Area właściwości . Następnie wykonuje sortowanie pomocnicze przy użyciu Population właściwości .

IEnumerable<Country> querySortedCountries =
    from country in countries
    orderby country.Area, country.Population descending
    select country;

Słowo kluczowe jest opcjonalne. Jest ascending to domyślna kolejność sortowania, jeśli nie określono żadnego zamówienia. Aby uzyskać więcej informacji, zobacz klauzulę orderby.

Klauzula join

Użyj klauzuli join , aby skojarzyć i/lub połączyć elementy z jednego źródła danych z elementami z innego źródła danych na podstawie porównania równości między określonymi kluczami w każdym elemecie. W linQ operacje sprzężenia są wykonywane na sekwencjach obiektów, których elementy są różnymi typami. Po połączeniu dwóch sekwencji należy użyć instrukcji select or group , aby określić, który element ma być przechowywany w sekwencji danych wyjściowych. Można również użyć typu anonimowego, aby połączyć właściwości z każdego zestawu skojarzonych elementów w nowy typ sekwencji danych wyjściowych. Poniższy przykład kojarzy prod obiekty, których Category właściwość pasuje do jednej z kategorii w tablicy categories ciągów. Produkty, których Category ciąg nie pasuje do żadnego ciągu, categories są odfiltrowane. Instrukcja select projektuje nowy typ, którego właściwości są pobierane zarówno z , cat jak i prod.

var categoryQuery =
    from cat in categories
    join prod in products on cat equals prod.Category
    select new
    {
        Category = cat,
        Name = prod.Name
    };

Można również wykonać sprzężenie grupy, przechowując wyniki join operacji w zmiennej tymczasowej przy użyciu słowa kluczowego into . Aby uzyskać więcej informacji, zobacz klauzulę join.

Klauzula Let

let Użyj klauzuli , aby zapisać wynik wyrażenia, takiego jak wywołanie metody, w nowej zmiennej zakresu. W poniższym przykładzie zmienna firstName zakresu przechowuje pierwszy element tablicy ciągów zwracanych przez Splitelement .

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

Aby uzyskać więcej informacji, zobacz klauzulę let.

Podzapytania w wyrażeniu zapytania

Klauzula zapytania może zawierać wyrażenie zapytania, które jest czasami określane jako podzapytywanie. Każde podzapytywanie rozpoczyna się od własnej from klauzuli, która niekoniecznie wskazuje to samo źródło danych w pierwszej from klauzuli. Na przykład następujące zapytanie przedstawia wyrażenie zapytania, które jest używane w instrukcji select w celu pobrania wyników operacji grupowania.

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()
    };

Aby uzyskać więcej informacji, zobacz Wykonywanie podzapytania operacji grupowania.

Zobacz też