クエリ式の基本Query expression basics
この記事では、C# でのクエリ式に関連する基本概念について説明します。This article introduces the basic concepts related to query expressions in C#.
クエリとは何か。またどのような働きをするのかWhat is a query and what does it do?
クエリとは、指定したデータ ソース (単一または複数) からどのようなデータを取得し、それらのデータをどのような形式と編成で返すかを説明した、命令のセットです。A query is a set of instructions that describes what data to retrieve from a given data source (or sources) and what shape and organization the returned data should have. クエリは、それが生成する結果とは区別されます。A query is distinct from the results that it produces.
一般に、ソース データは、同じ種類の要素のシーケンスとして論理的に編成されます。Generally, the source data is organized logically as a sequence of elements of the same kind. たとえば、SQL データベース テーブルには、行のシーケンスが含まれています。For example, a SQL database table contains a sequence of rows. XML ファイルには、XML 要素のシーケンスがあります (ただし、これらはツリー構造で階層化されています)。In an XML file, there is a "sequence" of XML elements (although these are organized hierarchically in a tree structure). メモリ内コレクションには、オブジェクトのシーケンスが含まれています。An in-memory collection contains a sequence of objects.
アプリケーションの観点から言うと、元のソース データの特定の型や構造体はは重要ではありません。From an application's viewpoint, the specific type and structure of the original source data is not important. アプリケーションは常に、ソース データを IEnumerable<T> または IQueryable<T> コレクションとして認識します。The application always sees the source data as an IEnumerable<T> or IQueryable<T> collection. たとえば、LINQ to XML では、ソース データは IEnumerable
<XElement> として表示されます。For example, in LINQ to XML, the source data is made visible as an IEnumerable
<XElement>.
クエリは、このソース シーケンスに対して、次の 3 つのうち、いずれかの操作を行います。Given this source sequence, a query may do one of three things:
個々 の要素を変更することなく、要素のサブセットを取得して新しいシーケンスを生成する。Retrieve a subset of the elements to produce a new sequence without modifying the individual elements. クエリはその後、次の例のように、返されたシーケンスをさまざまな方法で並べ替えたり、グループ化する場合があります (例では
scores
をint[]
と想定)。The query may then sort or group the returned sequence in various ways, as shown in the following example (assumescores
is anint[]
):IEnumerable<int> highScoresQuery = from score in scores where score > 80 orderby score descending select score;
上記の例のように要素のシーケンスを取得するが、それらを新しい型のオブジェクトに変換する。Retrieve a sequence of elements as in the previous example but transform them to a new type of object. たとえば、クエリでは、データ ソース内の特定の顧客レコードから姓だけを取得することがあります。For example, a query may retrieve only the last names from certain customer records in a data source. また、完全なレコードを取得し、それを使用して別のメモリ内オブジェクト型や XML データを構築した後、最終的な結果シーケンスを生成することもあります。Or it may retrieve the complete record and then use it to construct another in-memory object type or even XML data before generating the final result sequence. 次の例では、
int
からstring
へのプロジェクションを行っています。The following example shows a projection from anint
to astring
.highScoresQuery
の新しい型があることに注目してください。Note the new type ofhighScoresQuery
.IEnumerable<string> highScoresQuery2 = from score in scores where score > 80 orderby score descending select $"The score is {score}";
ソース データに関するシングルトン値を取得します。次に例を示します。Retrieve a singleton value about the source data, such as:
特定の条件に一致する要素の数。The number of elements that match a certain condition.
最大値または最小値を持つ要素。The element that has the greatest or least value.
条件に一致する最初の要素や、指定された要素セット内の特定の値の合計。The first element that matches a condition, or the sum of particular values in a specified set of elements. たとえば、次のクエリは、整数配列
scores
から、80 より大きいスコアの数を返します。For example, the following query returns the number of scores greater than 80 from thescores
integer array:int highScoreCount = (from score in scores where score > 80 select score) .Count();
上記の例では、
Count
メソッドに対する呼び出しの前に、クエリ式を囲むかっこが使用されています。In the previous example, note the use of parentheses around the query expression before the call to theCount
method. これは、具体的な結果を格納する新しい変数を使用しても表現できます。You can also express this by using a new variable to store the concrete result. この手法では、クエリを格納する変数が、結果を格納するクエリとは別に保持されるので、コードがより読みやすくなります。This technique is more readable because it keeps the variable that stores the query separate from the query that stores a result.IEnumerable<int> highScoresQuery3 = from score in scores where score > 80 select score; int scoreCount = highScoresQuery3.Count();
上記の例では、Count
に対する呼び出しの前でクエリが実行されています。これは、highScoresQuery
によって返された要素の数を確認するために、Count
が結果を反復処理する必要があるためです。In the previous example, the query is executed in the call to Count
, because Count
must iterate over the results in order to determine the number of elements returned by highScoresQuery
.
クエリ式とは何かWhat is a query expression?
クエリ式とは、クエリ構文で表されたクエリのことです。A query expression is a query expressed in query syntax. クエリ式は、ファーストクラスの言語コンストラクトです。A query expression is a first-class language construct. 他の式とよく似ていて、C# 式が有効である任意のコンテキストで使用できます。It is just like any other expression and can be used in any context in which a C# expression is valid. クエリ式の構文は、SQL や XQuery などのような宣言型の構文で記述された、一連の句で構成されます。A query expression consists of a set of clauses written in a declarative syntax similar to SQL or XQuery. 各句には 1 つ以上の C# 式が含まれていて、それらの式は、それ自体がクエリ式である場合もあれば、クエリ式を含んでいる場合もあります。Each clause in turn contains one or more C# expressions, and these expressions may themselves be either a query expression or contain a query expression.
クエリ式は from 句で始まり、select または group 句で終わる必要があります。A query expression must begin with a from clause and must end with a select or group clause. 最初の from
句と最後の select
または group
句の間には、次の省略可能句を 1 つ以上含めることができます: where、orderby、join、let、および追加の from 句。Between the first from
clause and the last select
or group
clause, it can contain one or more of these optional clauses: where, orderby, join, let and even additional from clauses. また、into キーワードを使用して、join
句や group
句の結果を、同じクエリ式内の追加のクエリ句のソースとして使用することもできます。You can also use the into keyword to enable the result of a join
or group
clause to serve as the source for additional query clauses in the same query expression.
クエリ変数Query variable
LINQ では、クエリの結果ではなく、クエリを格納する変数を、クエリ変数と呼びます。In LINQ, a query variable is any variable that stores a query instead of the results of a query. より具体的に言うと、クエリ変数は常に列挙可能な型であり、foreach
ステートメントか、または IEnumerator.MoveNext
メソッドに対する直接呼び出しで反復処理された場合に、要素のシーケンスを生成します。More specifically, a query variable is always an enumerable type that will produce a sequence of elements when it is iterated over in a foreach
statement or a direct call to its IEnumerator.MoveNext
method.
次のコード例は、データ ソース、フィルター句、および並べ替え句がそれぞれ 1 つずつあり、ソース要素の変換がない、簡単なクエリ式を示したものです。The following code example shows a simple query expression with one data source, one filtering clause, one ordering clause, and no transformation of the source elements. select
句でクエリが終わっています。The select
clause ends the query.
static void Main()
{
// 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 (int testScore in scoreQuery)
{
Console.WriteLine(testScore);
}
}
// Outputs: 93 90 82 82
上記の例では、scoreQuery
が クエリ変数です。クエリ変数は単にクエリと呼ばれることもあります。In the previous example, scoreQuery
is a query variable, which is sometimes referred to as just a query. クエリ変数には、foreach
ループで生成された実際の結果データは格納されません。The query variable stores no actual result data, which is produced in the foreach
loop. また、foreach
ステートメントが実行されたとき、クエリ結果はクエリ変数 scoreQuery
を通じては返されません。And when the foreach
statement executes, the query results are not returned through the query variable scoreQuery
. 結果は反復変数 testScore
を通じて返されます。Rather, they are returned through the iteration variable testScore
. scoreQuery
変数は 2 番目の foreach
ループで反復処理できます。The scoreQuery
variable can be iterated in a second foreach
loop. この変数とデータ ソースのどちらかが変更されないかぎり、同じ結果が生成されます。It will produce the same results as long as neither it nor the data source has been modified.
クエリ変数には、クエリ構文、メソッド構文、またはそれら 2 つの組合せで表現されたクエリが格納される場合があります。A query variable may store a query that is expressed in query syntax or method syntax, or a combination of the two. 次の例では、queryMajorCities
と queryMajorCities2
の両方がクエリ変数です。In the following examples, both queryMajorCities
and queryMajorCities2
are query variables:
//Query syntax
IEnumerable<City> queryMajorCities =
from city in cities
where city.Population > 100000
select city;
// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);
これに対し、次の 2 つの例は、クエリで初期化されてはいるものの、クエリ変数ではない変数を示しています。On the other hand, the following two examples show variables that are not query variables even though each is initialized with a query. これらは結果を格納するので、クエリ変数ではありません。They are not query variables because they store results:
int highestScore =
(from score in scores
select score)
.Max();
// or split the expression
IEnumerable<int> scoreQuery =
from score in scores
select score;
int highScore = scoreQuery.Max();
// the following returns the same result
int highScore = scores.Max();
List<City> 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;
List<City> largeCitiesList2 = largeCitiesQuery.ToList();
クエリのさまざまな表現方法については、「LINQ でのクエリ構文とメソッド構文」をご覧ください。For more information about the different ways to express queries, see Query syntax and method syntax in LINQ.
クエリ変数の明示的型指定と暗黙的型指定Explicit and implicit typing of query variables
このドキュメントでは通常、明示的な型のクエリ変数で説明を行います。これは、クエリ変数と select 句の関係を示すためです。This documentation usually provides the explicit type of the query variable in order to show the type relationship between the query variable and the select clause. ただし、 var キーワードを使用すれば、コンパイル時にクエリ変数 (またはその他のローカル変数) の型を推論するようにコンパイラに指示することもできます。However, you can also use the var keyword to instruct the compiler to infer the type of a query variable (or any other local variable) at compile time. たとえば、このトピックで先に示したクエリの例は、暗黙的な型指定を使用しても表現できます。For example, the query example that was shown previously in this topic can also be expressed by using implicit typing:
// Use of var is optional here and in all queries.
// queryCities is an IEnumerable<City> just as
// when it is explicitly typed.
var queryCities =
from city in cities
where city.Population > 100000
select city;
詳しくは、「暗黙的に型指定されるローカル変数」および「LINQ クエリ操作での型の関係」をご覧ください。For more information, see Implicitly typed local variables and Type relationships in LINQ query operations.
クエリ式の開始Starting a query expression
クエリ式は、from
句で始める必要があります。A query expression must begin with a from
clause. この句では、データ ソースと範囲変数を指定します。It specifies a data source together with a range variable. 範囲変数は、ソース シーケンスが走査されるときの、ソース シーケンス内の連続する各要素を表します。The range variable represents each successive element in the source sequence as the source sequence is being traversed. 範囲変数は、データ ソース内の要素の型に基づいて厳密に型指定されます。The range variable is strongly typed based on the type of elements in the data source. 次の例では、countries
が Country
オブジェクトの配列であるため、範囲変数も Country
として型指定されています。In the following example, because countries
is an array of Country
objects, the range variable is also typed as Country
. 範囲変数は厳密に型指定されるので、ドット演算子を使用して、その型の利用可能なメンバーにアクセスすることができます。Because the range variable is strongly typed, you can use the dot operator to access any available members of the type.
IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > 500000 //sq km
select country;
範囲変数は、クエリがセミコロンまたは continuation 句で終了するまでスコープ内に維持されます。The range variable is in scope until the query is exited either with a semicolon or with a continuation clause.
クエリ式には、複数の from
句を含めることができます。A query expression may contain multiple from
clauses. ソース シーケンス内の各要素がそれ自体コレクションであるか、またはコレクションを格納している場合には、追加の from
句を使用します。Use additional from
clauses when each element in the source sequence is itself a collection or contains a collection. たとえば、Country
オブジェクトのコレクションがあり、各オブジェクトに、Cities
という名前の City
オブジェクトのコレクションが格納されているとします。For example, assume that you have a collection of Country
objects, each of which contains a collection of City
objects named Cities
. その場合、各 Country
内の City
オブジェクトを照会するには、次のように 2 つの from
句を使用します。To query the City
objects in each Country
, use two from
clauses as shown here:
IEnumerable<City> cityQuery =
from country in countries
from city in country.Cities
where city.Population > 10000
select city;
詳しくは、「from 句」をご覧ください。For more information, see from clause.
クエリ式の終了Ending a query expression
クエリ式は、group
句または select
句のいずれかで終わる必要があります。A query expression must end with either a group
clause or a select
clause.
group 句group clause
group
句は、指定したキーによって編成されたグループのシーケンスを生成するために使用します。Use the group
clause to produce a sequence of groups organized by a key that you specify. キーには、任意のデータ型を指定できます。The key can be any data type. たとえば、次のクエリでは、1 つ以上の Country
オブジェクトを含み、キーが char
値であるグループのシーケンスが作成されます。For example, the following query creates a sequence of groups that contains one or more Country
objects and whose key is a char
value.
var queryCountryGroups =
from country in countries
group country by country.Name[0];
グループ化について詳しくは、「group 句」をご覧ください。For more information about grouping, see group clause.
select 句select clause
select
句は、その他すべての型のシーケンスを生成するために使用します。Use the select
clause to produce all other types of sequences. シンプルな select
句は、データ ソース内に含まれるオブジェクトと同じ型のオブジェクトのシーケンスを生成します。A simple select
clause just produces a sequence of the same type of objects as the objects that are contained in the data source. この例では、データ ソースに Country
オブジェクトが含まれています。In this example, the data source contains Country
objects. orderby
句は要素を新しい順序に並べ替え、select
句は並べ替えられた Country
オブジェクトのシーケンスを生成します。The orderby
clause just sorts the elements into a new order and the select
clause produces a sequence of the reordered Country
objects.
IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
select
句は、ソース データを新しい型のシーケンスに変換するために使用できます。The select
clause can be used to transform source data into sequences of new types. この変換は、プロジェクションとも呼ばれます。This transformation is also named a projection. 次の例では、select
句は元の要素内にあるフィールドのサブセットのみを含んだ、匿名型のシーケンスをプロジェクトします。In the following example, the select
clause projects a sequence of anonymous types which contains only a subset of the fields in the original element. 新しいオブジェクトはオブジェクト初期化子を使用して初期化されています。Note that the new objects are initialized by using an object initializer.
// Here var is required because the query
// produces an anonymous type.
var queryNameAndPop =
from country in countries
select new { Name = country.Name, Pop = country.Population };
select
句を使用してソース データを変換する方法について詳しくは、「select 句」をご覧ください。For more information about all the ways that a select
clause can be used to transform source data, see select clause.
"into" を使用した継続Continuations with "into"
select
句または group
句で into
キーワードを使用すると、クエリを格納する一時的な識別子を作成できます。You can use the into
keyword in a select
or group
clause to create a temporary identifier that stores a query. これは、grouping 操作や select 操作の後、クエリに対する追加のクエリ操作を実行する必要がある場合に便利です。Do this when you must perform additional query operations on a query after a grouping or select operation. 次の例では、1 千万という範囲の人口で countries
をグループ化しています。In the following example countries
are grouped according to population in ranges of 10 million. これらのグループが作成された後、追加の句で一部のグループを除外し、その後、グループを昇順で並べ替えようとしています。After these groups are created, additional clauses filter out some groups, and then to sort the groups in ascending order. これらの追加操作を実行するには、countryGroup
によって継続を表す必要があります。To perform those additional operations, the continuation represented by countryGroup
is required.
// 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);
}
詳しくは、「into」をご覧ください。For more information, see into.
フィルター処理、並べ替え、および結合Filtering, ordering, and joining
開始の from
句と、終了の select
またはgroup
句の間には、その他のすべての省略可能句 (where
、join
、orderby
、from
、let
) を必要に応じて使用できます。Between the starting from
clause, and the ending select
or group
clause, all other clauses (where
, join
, orderby
, from
, let
) are optional. 省略可能句は、クエリ本文で任意の回数 (0 回~複数回) 使用できます。Any of the optional clauses may be used zero times or multiple times in a query body.
where 句where clause
where
句は、1 つ以上の述語式に基づいて、ソース データから要素を除外するために使用します。Use the where
clause to filter out elements from the source data based on one or more predicate expressions. 次の例では、where
句に 1 つの述語と 2 つの条件があります。The where
clause in the following example has one predicate with two conditions.
IEnumerable<City> queryCityPop =
from city in cities
where city.Population < 200000 && city.Population > 100000
select city;
詳しくは、「where 句」をご覧ください。For more information, see where clause.
orderby 句orderby clause
orderby
句は、結果を昇順または降順で並べ替えるために使用します。Use the orderby
clause to sort the results in either ascending or descending order. 第 2 の並べ替え順序を指定することもできます。You can also specify secondary sort orders. 次の例では、Area
プロパティを使用して、country
オブジェクトに対する 第 1 の並べ替えを実行しています。The following example performs a primary sort on the country
objects by using the Area
property. その後、Population
プロパティを使用して第 2 の並べ替えを実行しています。It then performs a secondary sort by using the Population
property.
IEnumerable<Country> querySortedCountries =
from country in countries
orderby country.Area, country.Population descending
select country;
ascending
キーワードは省略可能です。順序が指定されていない場合は、これが既定の並べ替え順序になります。The ascending
keyword is optional; it is the default sort order if no order is specified. 詳しくは、「orderby 句」をご覧ください。For more information, see orderby clause.
join 句join clause
join
句は、各要素内の指定したキー間での等値比較に基づいて、1 つのデータ ソースの要素を別のデータ ソースの要素と関連付けたり、組み合わせたりするために使用します。Use the join
clause to associate and/or combine elements from one data source with elements from another data source based on an equality comparison between specified keys in each element. LINQ では、join 操作は要素の型が異なるオブジェクトのシーケンスに対して実行されます。In LINQ, join operations are performed on sequences of objects whose elements are different types. 2 つのシーケンスを結合した後には、select
または group
ステートメント使用して、出力シーケンスに格納する要素を指定する必要があります。After you have joined two sequences, you must use a select
or group
statement to specify which element to store in the output sequence. また、匿名型を使用して、関連付けられた各要素セットのプロパティを、出力シーケンス用の新しい型に結合することもできます。You can also use an anonymous type to combine properties from each set of associated elements into a new type for the output sequence. 次の例では、Category
プロパティが categories
文字列配列内のいずれかのカテゴリと一致する prod
オブジェクトを関連付けています。The following example associates prod
objects whose Category
property matches one of the categories in the categories
string array. Category
が categories
内の文字列に一致しない製品は除外されます。select
ステートメントは、cat
と prod
の両方からプロパティを取った新しい型をプロジェクトしています。Products whose Category
does not match any string in categories
are filtered out. The select
statement projects a new type whose properties are taken from both cat
and prod
.
var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
select new { Category = cat, Name = prod.Name };
into キーワードを使用して join
操作の結果を一時変数に格納することにより、グループ結合を実行することもできます。You can also perform a group join by storing the results of the join
operation into a temporary variable by using the into keyword. 詳しくは、「join 句」をご覧ください。For more information, see join clause.
let 句let clause
let
句は、式の結果 (メソッド呼び出しなど) を新しい範囲変数に格納するために使用します。Use the let
clause to store the result of an expression, such as a method call, in a new range variable. 次の例では、Split
よって返された文字列配列の最初の要素を、範囲変数 firstName
に格納しています。In the following example, the range variable firstName
stores the first element of the array of strings that is returned by 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 (string s in queryFirstNames)
Console.Write(s + " ");
//Output: Svetlana Claire Sven Cesar
詳しくは、「let 句」をご覧ください。For more information, see let clause.
クエリ式内のサブクエリSubqueries in a query expression
クエリ句には、それ自体にクエリ式が含まれることがあります。これは、サブクエリとも呼ばれます。A query clause may itself contain a query expression, which is sometimes referred to as a subquery. 各サブクエリは、独自の from
で始まります。この句は、最初の from
句と必ずしも同じデータ ソースを指している必要はありません。Each subquery starts with its own from
clause that does not necessarily point to the same data source in the first from
clause. たとえば、次のクエリは、select ステートメントで grouping 操作の結果を取得するために使用されるクエリ式を示しています。For example, the following query shows a query expression that is used in the select statement to retrieve the results of a grouping operation.
var queryGroupMax =
from student in students
group student by student.GradeLevel into studentGroup
select new
{
Level = studentGroup.Key,
HighestScore =
(from student2 in studentGroup
select student2.Scores.Average())
.Max()
};
詳細については、「グループ化操作でのサブクエリの実行」を参照してください。For more information, see Perform a subquery on a grouping operation.