HOWTO · Csharp

C# を使用した LINQ クエリの複数の列によるグループ化

これは、LINQ クエリの使用と、LINQ クエリを使用して列ごとにグループ化する方法についての記事です。

この記事では、C# を使用した LINQ クエリについて簡単に紹介します。さらに、LINQ クエリを使用して結果を複数の列でグループ化する方法についても説明します。

すでに LINQ に精通している場合は、導入セクションをスキップしても問題ありません。

LINQ の概要

統合言語クエリの略である LINQ は、データベース、配列、XML などのさまざまなデータソースからのデータにアクセスするための統一された方法を提供する方法です。すべてのクエリをそれ自体で統合できます。

開発者がアプリケーションを開発するとき、データソースからデータを取得するには、プログラミング言語以外の追加の知識が必要です。たとえば、データソースがデータベースの場合、プログラマーは SQL の知識が必要です。

同様に、プログラマーは、データソースが XML ドキュメントである場合に XML を解析する方法を知っている必要があります。一方、LINQ では、これらすべてのデータソースからデータを取得するために必要なのは LINQ に関する知識だけです。

LINQ アーキテクチャ

オブジェクトへの LINQ

LINQ to Objects は、LINQ クエリを使用してメモリ内のデータ構造からデータを取得できることを意味します。このデータ構造は、ユーザー定義および一部の DotNet 定義の API にすることができます。

データ構造の唯一の要件は、コレクションを IEnummerable<T> タイプで返す必要があることです。

オブジェクトへの LINQ の例を考えてみましょう。

using System;
using System.Linq;

class MyProgram {
  static void Main() {
    string[] list = { "apple", "ball", "aeroplane", "beautiful", "ancient" };

    var starts = from w in list
                 where w.StartsWith("a") select w;
    // Print words
    foreach (var word in starts) {
      Console.WriteLine(word);
    }
  }
}

出力:

apple
aeroplane
ancient

上記のコードスニペットでは、文字列配列からデータにアクセスするための LINQ クエリを作成しました。このようにして、この配列を任意のデータ構造に置き換えることができます。

LINQ クエリの利点は、後ろのデータ構造を変更しても、構文が同じままになることです。クエリは同じままです。これは、LINQ によって提供される均一性です。

LINQ から SQL へ

LINQ to ADO.Net の場合、3つのサブコンポーネントがあります。ただし、主に LINQtoSQL に焦点を当てます。

LINQ to SQL を使用すると、リレーショナルデータベースをオブジェクトに変換できます。これにより、データ操作がはるかに簡単かつ高速になります。

それに続くプロセスは、最初にデータベースに接続し、LINQ クエリを SQL クエリに変換してから、それらの SQL クエリを実行することです。SQL から返された結果は、LINQ によって構築されたオブジェクトに変換されてから、ユーザーに返されます。

LINQtoSQL プロセス

LINQ はまた、オブジェクトのデータの変更をトレースし、データベース内のそれらの変更を自動的に同期します。

プロジェクトに LINQ to SQL コンポーネントを作成すると、すべてのデータベーステーブルのクラスが自動的に作成されます。その後、接続およびデータベース操作のコーディングを行う必要があります。

会社の従業員のデータを格納するためのテーブルがあるとします。テーブル名は Emp で、フィールド IdName、および Email が含まれています。

LINQ クエリとして使用するには、次のコードスニペットを検討してください。

using System;
using System.Linq;

namespace MyLINQExample {
   class LINQExample {
      static void Main(string[] args) {

         string connectString = System.Configuration.ConfigurationManager.            ConnectionStrings["LinqToSQLDBConnectionString"].ToString();
         LinqToSQLDataContext db = new LinqToSQLDataContext(connectString);
         Emp newEmp = new Emp();
         newEmp.name = "John";
         newEmp.email = "john@abccompany.com";
         newEmp.id = 3;
         //Add this new employee to the database
         db.Emps.InsertOnSubmit(newEmp);

         //To save changes in the database
         db.SubmitChanges();

         //Get the data of inserted employee
         Emp e = db.Emps.FirstOrDefault(e e.name.Equals("John"));

         Console.WriteLine("Emp Id = {0} , Name = {1}, Email = {2}",
                          e.id, e.name, e.email);

         Console.WriteLine("\nPress any key to continue.");
         Console.ReadKey();
      }
   }
}

上記のコードでは、データベースに接続し、データコンテキストオブジェクトを作成してから、データベースでクエリを実行しました。

出力:

Emp Id = 3, Name = John, Email = john@abccompany.com

LINQ での結合

単純な SQL クエリと同様に、LINQ クエリを使用して列を結合することもできます。結合操作は、ある条件に基づいて異なるテーブルからのデータが必要な場合に実行されます。

LINQ は、単一または複数の列を簡単に結合できる join 演算子を提供します。次の 2つのクラスがあるとします。

public class Student {
  public int Stu_ID { get; set; }
  public string Stu_Name { get; set; }
  public int Class_ID { get; set; }
}

public class Grade {
  public int Grade_ID { get; set; }
  public string Grade_Name { get; set; }
}

ドライバー関数で、次のようにクラスごとに 2つのリストを作成しましょう。

IList<Student> students_list = new List<Student>() {
  new Student() { Stu_ID = 11, Stu_Name = "ABC", Class_ID = 1 },
  new Student() { Stu_ID = 12, Stu_Name = "DEF", Class_ID = 1 },
  new Student() { Stu_ID = 13, Stu_Name = "GHI", Class_ID = 2 },
  new Student() { Stu_ID = 14, Stu_Name = "JKL", Class_ID = 2 },
};

IList<Grade> gradeList = new List<Grade>() { new Grade() { Grade_ID = 1, Grade_Name = "Grade 1" },
                                             new Grade() { Grade_ID = 2, Grade_Name = "Grade 2" },
                                             new Grade() { Grade_ID = 3, Grade_Name = "Grade 3" } };

さて、生徒とその学年の名前を取得したい場合は、これら 2つのテーブルを結合する必要があります。これらのテーブルは、それぞれ Class_IDGrade_ID に基づいて結合されます。

結合クエリは次のようになります。

var joinResult =
    from s in student_list join g in gradeList on s.Class_ID equals g.Grade_ID select new {
      StuName = s.Stu_Name, GradeName = g.Grade_Name
    };

これにより、次の出力が生成されます。

ABC Grade 1
DEF Grade 1
GHI Grade 2
JKL Grade 2

C# を使用した LINQ クエリの複数の列によるグループ化

同様に、いくつかの属性に基づいてデータをグループ化することもできます。これは、LINQ クエリの GroupBy 句を使用して行われます。

GroupBy 演算子は、キー値に基づいて、提供されたコレクション内の要素のサブセットを返します。IGrouping<TKey, TElement> オブジェクトは各グループを表します。

さらに、GroupBy メソッドはさまざまなオーバーロードメソッドをサポートしているため、要件に基づいてメソッド構文で適切な拡張メソッドを利用できます。

次のコードを検討してください。

List<Student> student_List = new List<Student>() {
  new Student() { Stu_ID = 11, Stu_Name = "ABC", Age = 18, Subject = "Arts" },
  new Student() { Stu_ID = 12, Stu_Name = "DEF", Age = 19, Subject = "Science" },
  new Student() { Stu_ID = 13, Stu_Name = "GHI", Age = 18, Subject = "Arts" },
  new Student() { Stu_ID = 14, Stu_Name = "JKL", Age = 19, Subject = "Science" },
};

学生のリストを年齢別にグループ化したいとします。次のクエリは私たちのためにそれを行います:

var result = from s in student_List group s by s.Age;

foreach (var ageGroups in result) {
  Console.WriteLine("Age Group: {0}", ageGroups.Key);
  foreach (Student s in ageGroups)  // Each group has inner collection
    Console.WriteLine("Student Name: {0}", s.Stu_Name);
}

出力:

Age Group: 18
Student Name: ABC
Student Name: GHI
Age Group: 19
Student Name: DEF
Student Name: JKL

同様に、複数の列でグループ化することもできます。これは次の方法で行われます。

var result = from s in student_List group s by new { s.Age, s.Subject };
foreach (var ageGroups in groupedResult) {
  Console.WriteLine("Group: {0}", ageGroups.Key);

  foreach (Student s in ageGroups)  // Each group has inner collection
    Console.WriteLine("Student Name: {0}", s.Stu_Name);
}

このクエリにより、次の出力が生成されます。

Group: {Age= 18, Subject="Arts"}
Student Name: ABC
Student Name: GHI
Group: {Age= 19, Subject="Science"}
Student Name: DEF
Student Name: JKL