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 IEnumerable
XElement<>.
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 toint[]
):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 elementuhighScoresQuery
.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 highScoresQuery
element .
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 scoreQuery
zapytania . Zamiast tego są one zwracane za pośrednictwem zmiennej testScore
iteracji . 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 Country
obiekcie , 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 select
projektuje 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 Split
element .
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.