クエリ式の基本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. クエリはその後、次の例のように、返されたシーケンスをさまざまな方法で並べ替えたり、グループ化する場合があります (例では scoresint[] と想定)。The query may then sort or group the returned sequence in various ways, as shown in the following example (assume scores is an int[]):

    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 an int to a string. highScoresQuery の新しい型があることに注目してください。Note the new type of highScoresQuery.

    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 the scores integer array:

      int highScoreCount =
          (from score in scores
           where score > 80
           select score)

      上記の例では、Count メソッドに対する呼び出しの前に、クエリ式を囲むかっこが使用されています。In the previous example, note the use of parentheses around the query expression before the call to the Count 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 つ以上含めることができます: whereorderbyjoinlet、および追加の 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)
// 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. 次の例では、queryMajorCitiesqueryMajorCities2 の両方がクエリ変数です。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)

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

// 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. 次の例では、countriesCountry オブジェクトの配列であるため、範囲変数も 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)
    foreach (var country in grouping)
        Console.WriteLine(country.Name + ":" + country.Population);

詳しくは、「into」をご覧ください。For more information, see into.

フィルター処理、並べ替え、および結合Filtering, ordering, and joining

開始の from 句と、終了の select またはgroup句の間には、その他のすべての省略可能句 (wherejoinorderbyfromlet) を必要に応じて使用できます。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. Categorycategories 内の文字列に一致しない製品は除外されます。select ステートメントは、catprod の両方からプロパティを取った新しい型をプロジェクトしています。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())

詳細については、「グループ化操作でのサブクエリの実行」を参照してください。For more information, see Perform a subquery on a grouping operation.

関連項目See also