C# DataTableの使い方完全ガイド|作成・追加・検索・並び替え・LINQまで初心者向けに解説
はじめに
C#で表形式のデータを扱うときによく使われるのがDataTableです。DataTableは、行と列で構成されたデータをメモリ上で管理できるクラスで、データベースの検索結果、CSVデータ、画面表示用の一覧データなどを扱う場面でよく利用されます。
C#を学び始めたばかりの方にとって、DataTableは少し難しく感じるかもしれません。DataRow、DataColumn、DataSet、DataViewなど関連するクラスも多く、どれをどの場面で使えばよいのか迷いやすいからです。
この記事では、C#のDataTableについて、作成、列の追加、データの追加、取得、検索、並び替え、更新、削除、LINQでの操作、CSVやデータベースとの連携まで、初心者にもわかりやすく解説します。
基本的な使い方から実践的な注意点まで順番に説明するので、c# datatableの使い方を一通り理解したい方は、ぜひ参考にしてください。
1. C#のDataTableとは?初心者向けに役割と使いどころを解説
1-1. DataTableとは表形式のデータを扱うためのクラス
DataTableとは、C#で表形式のデータをメモリ上に保持するためのクラスです。
表形式のデータとは、Excelやデータベースのテーブルのように、列と行で構成されたデータのことです。
例えば、次のような社員一覧を考えてみましょう。
| Id | Name | Age | Department |
|---|---|---|---|
| 1 | 佐藤 | 30 | 営業部 |
| 2 | 鈴木 | 25 | 開発部 |
| 3 | 田中 | 35 | 総務部 |
このようなデータをC#のプログラム内で扱いたい場合に、DataTableを使うと便利です。
DataTableでは、列をDataColumn、行をDataRowとして管理します。データベースから取得した結果を一時的に保存したり、CSVを読み込んで加工したり、画面の一覧表示に渡したりする用途でよく使われます。
1-2. DataTable・DataRow・DataColumn・DataSetの違い
DataTableを理解するには、関連するクラスとの違いを知っておくことが大切です。
DataTableは、1つの表を表します。列と行を持ち、データを格納します。
DataColumnは、DataTableの列を表します。列名やデータ型、NULLを許可するかどうか、初期値などを設定できます。
DataRowは、DataTableの1行分のデータを表します。各列に対応する値を保持します。
DataSetは、複数のDataTableをまとめて管理するためのクラスです。複数のテーブルを扱う場合や、テーブル同士の関係を管理したい場合に使います。
イメージとしては、次のようになります。
C#DataSet
├─ DataTable
│ ├─ DataColumn
│ ├─ DataColumn
│ └─ DataRow
└─ DataTable
├─ DataColumn
└─ DataRow
1つの表だけを扱うならDataTableで十分です。複数の表をまとめて扱う必要がある場合はDataSetを検討します。
1-3. DataTableを使うメリットと注意点
DataTableを使うメリットは、表形式のデータを柔軟に扱えることです。
列を動的に追加でき、行の追加、検索、並び替え、絞り込み、更新、削除などを簡単に行えます。また、データベースやCSV、画面表示部品との相性もよく、既存の業務アプリケーションでもよく使われています。
一方で、注意点もあります。
DataTableは柔軟な反面、型安全性が弱くなりやすいです。列名を文字列で指定するため、列名を間違えると実行時エラーになります。また、値の取得時にキャストやDBNullの扱いを間違えると例外が発生することがあります。
さらに、大量データを扱う場合はメモリ使用量や処理速度に注意が必要です。単純なデータ管理であれば、List<T>や配列の方がわかりやすく高速な場合もあります。
1-4. DataTableがよく使われる場面
DataTableは、次のような場面でよく使われます。
データベースから取得した結果を格納する場面では、SQLの検索結果をDataTableに読み込んで、画面表示や加工処理に利用できます。
CSVファイルを読み込む場面では、CSVの各列をDataColumn、各行をDataRowとして扱うことで、表データとして処理できます。
Windows FormsのDataGridViewに一覧表示する場面でも、DataTableをそのままデータソースとして指定できます。
また、帳票出力、Excel出力、マスターデータの一時保持、データ加工処理などでも使われます。
特に業務システムでは、データベース、一覧画面、CSV出力の間で表形式のデータを受け渡す機会が多いため、DataTableは今でもよく登場します。
1-5. Listや配列、Dictionaryとの使い分け
C#で複数のデータを扱う方法には、DataTable以外にもList<T>、配列、Dictionary<TKey, TValue>などがあります。
List<T>は、同じ型のオブジェクトを複数管理する場合に適しています。例えば、社員情報をEmployeeクラスとして定義し、List<Employee>で扱う方法です。型安全で、LINQとも相性がよく、現在のC#ではよく使われる方法です。
配列は、要素数が固定されているデータを扱う場合に向いています。ただし、追加や削除が多い場合はList<T>の方が扱いやすいです。
Dictionary<TKey, TValue>は、キーと値の組み合わせでデータを管理する場合に使います。例えば、社員IDをキーにして社員名を取得するような用途に向いています。
DataTableは、列構成が動的に変わる場合や、データベース、CSV、画面表示と連携しやすい形で表データを扱いたい場合に便利です。
一方で、アプリケーション内部で型安全にデータを扱いたい場合は、List<T>の方が適していることも多いです。
2. DataTableを使うための準備
2-1. System.Data名前空間を追加する
DataTableを使うには、System.Data名前空間を使用します。
C#ファイルの先頭に次のusingを追加します。
C#using System.Data;
これにより、DataTable、DataRow、DataColumn、DataSet、DataViewなどのクラスを使えるようになります。
基本的なコードは次のようになります。
C#using System;
using System.Data;
class Program
{
static void Main()
{
DataTable table = new DataTable();
Console.WriteLine("DataTableを作成しました。");
}
}
2-2. .NET Frameworkと.NETの違いによる注意点
DataTableは、.NET Frameworkでも、現在の.NETでも使用できます。
ただし、プロジェクトの種類やターゲットフレームワークによっては、参照設定やパッケージが必要になる場合があります。通常のコンソールアプリやWindows Formsアプリでは、System.Dataを追加すれば利用できることが多いです。
また、DataTableにLINQを使う場合は、System.Data.DataSetExtensionsが必要になることがあります。AsEnumerableやField<T>、CopyToDataTableを使う場合は、次の名前空間を追加します。
C#using System.Linq;
using System.Data;
環境によっては、System.Data.DataSetExtensionsへの参照が必要です。
もしAsEnumerableが見つからない場合は、プロジェクトに必要な参照やパッケージが追加されているか確認しましょう。
2-3. DataTableの基本的な書き方
DataTableを使う基本的な流れは、次の通りです。
まず、DataTableを作成します。次に、列を追加します。その後、行を追加します。最後に、追加したデータを取得したり表示したりします。
C#DataTable table = new DataTable("Employees");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
table.Rows.Add(1, "佐藤", 30);
table.Rows.Add(2, "鈴木", 25);
table.Rows.Add(3, "田中", 35);
foreach (DataRow row in table.Rows)
{
Console.WriteLine($"{row["Id"]}: {row["Name"]} ({row["Age"]}歳)");
}
実行結果は次のようになります。
1: 佐藤 (30歳)
2: 鈴木 (25歳)
3: 田中 (35歳)
このように、DataTableでは列を定義してから行データを追加するのが基本です。
2-4. サンプルで使用するデータ構成
この記事では、主に社員情報を例にして解説します。
使用する列は次の通りです。
| 列名 | 型 | 内容 |
|---|---|---|
| Id | int | 社員ID |
| Name | string | 氏名 |
| Age | int | 年齢 |
| Department | string | 部署 |
| JoinDate | DateTime | 入社日 |
サンプルのDataTableを作成するメソッドを用意しておくと、以降の説明で使い回せます。
C#static DataTable CreateEmployeeTable()
{
DataTable table = new DataTable("Employees");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
table.Columns.Add("Department", typeof(string));
table.Columns.Add("JoinDate", typeof(DateTime));
table.Rows.Add(1, "佐藤", 30, "営業部", new DateTime(2020, 4, 1));
table.Rows.Add(2, "鈴木", 25, "開発部", new DateTime(2022, 1, 15));
table.Rows.Add(3, "田中", 35, "総務部", new DateTime(2018, 7, 10));
table.Rows.Add(4, "高橋", 28, "開発部", new DateTime(2021, 10, 1));
return table;
}
3. C#でDataTableを作成する方法
3-1. 空のDataTableを作成する
空のDataTableは、次のように作成します。
C#DataTable table = new DataTable();
この時点では、テーブル名も列も行もありません。
列を追加しないまま行を追加しようとすると、正しくデータを格納できません。通常は、DataTableを作成したあとにColumns.Addで列を追加します。
C#DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Rows.Add(1, "佐藤");
3-2. テーブル名を指定して作成する
DataTableにはテーブル名を指定できます。
C#DataTable table = new DataTable("Employees");
テーブル名は、TableNameプロパティでも設定できます。
C#DataTable table = new DataTable();
table.TableName = "Employees";
テーブル名は必須ではありませんが、複数のDataTableを扱う場合や、DataSetに追加する場合、デバッグ時に内容を確認する場合などに役立ちます。
C#Console.WriteLine(table.TableName);
3-3. DataColumnで列を追加する
DataTableに列を追加するには、DataColumnを使います。
簡単な方法は、Columns.Addに列名を指定する書き方です。
C#DataTable table = new DataTable();
table.Columns.Add("Name");
この場合、列の型は文字列として扱われます。
より明確に列を定義したい場合は、DataColumnを作成してから追加します。
C#DataColumn column = new DataColumn();
column.ColumnName = "Name";
column.DataType = typeof(string);
table.Columns.Add(column);
DataColumnを使うと、列名、データ型、NULL許可、初期値、自動採番などを細かく設定できます。
3-4. 列名とデータ型を指定する
実務では、列名とデータ型を指定して追加することが多いです。
C#DataTable table = new DataTable("Employees");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
table.Columns.Add("JoinDate", typeof(DateTime));
データ型を指定しておくと、意図しない値が入るのを防ぎやすくなります。
例えば、Age列をint型にしている場合、文字列を追加しようとするとエラーになります。
C#table.Rows.Add(1, "佐藤", 30, new DateTime(2020, 4, 1));
データ型を明確にすることは、DataTableを安全に使うための重要なポイントです。
3-5. 主キーを設定する
DataTableでは、主キーを設定できます。
主キーを設定すると、Rows.Findで高速に行を検索できるようになります。また、主キー列には重複した値を登録できません。
C#DataTable table = new DataTable("Employees");
DataColumn idColumn = table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.PrimaryKey = new DataColumn[] { idColumn };
table.Rows.Add(1, "佐藤");
table.Rows.Add(2, "鈴木");
主キーを設定したあと、同じIdを追加しようとすると例外が発生します。
C#// エラーになる
table.Rows.Add(1, "田中");
主キー検索を使いたい場合や、データの一意性を保証したい場合は、主キーを設定しておきましょう。
3-6. AutoIncrementで連番IDを設定する
AutoIncrementを使うと、ID列に連番を自動設定できます。
C#DataTable table = new DataTable("Employees");
DataColumn idColumn = new DataColumn("Id", typeof(int));
idColumn.AutoIncrement = true;
idColumn.AutoIncrementSeed = 1;
idColumn.AutoIncrementStep = 1;
table.Columns.Add(idColumn);
table.Columns.Add("Name", typeof(string));
table.Rows.Add(null, "佐藤");
table.Rows.Add(null, "鈴木");
table.Rows.Add(null, "田中");
foreach (DataRow row in table.Rows)
{
Console.WriteLine($"{row["Id"]}: {row["Name"]}");
}
実行結果は次のようになります。
1: 佐藤
2: 鈴木
3: 田中
AutoIncrementSeedは開始値、AutoIncrementStepは増加値です。
ID列を自動採番したい場合に便利です。
3-7. AllowDBNullやDefaultValueで列の制約を設定する
DataColumnでは、NULLを許可するかどうかや初期値を設定できます。
C#DataTable table = new DataTable("Employees");
DataColumn idColumn = table.Columns.Add("Id", typeof(int));
idColumn.AllowDBNull = false;
DataColumn nameColumn = table.Columns.Add("Name", typeof(string));
nameColumn.AllowDBNull = false;
DataColumn departmentColumn = table.Columns.Add("Department", typeof(string));
departmentColumn.DefaultValue = "未所属";
AllowDBNull = falseを設定すると、その列にDBNull.Valueを入れることができなくなります。
DefaultValueを設定すると、値を指定しなかった場合に初期値が入ります。
C#DataRow row = table.NewRow();
row["Id"] = 1;
row["Name"] = "佐藤";
table.Rows.Add(row);
Console.WriteLine(row["Department"]);
実行結果は次のようになります。
未所属
必須項目や初期値がある場合は、列定義の段階で制約を設定しておくと安全です。
4. DataTableにデータを追加する方法
4-1. NewRowでDataRowを作成して追加する
DataTableに行を追加する基本的な方法は、NewRowでDataRowを作成し、値を設定してからRows.Addする方法です。
C#DataTable table = new DataTable("Employees");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
DataRow row = table.NewRow();
row["Id"] = 1;
row["Name"] = "佐藤";
row["Age"] = 30;
table.Rows.Add(row);
NewRowを使うと、列名を指定して値を設定できるため、どの列に何を入れているのかがわかりやすいです。
特に列数が多い場合や、条件によって値を設定する場合は、NewRowを使う書き方が安全です。
4-2. Rows.Addで直接データを追加する
Rows.Addを使うと、値を直接指定して行を追加できます。
C#DataTable table = new DataTable("Employees");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
table.Rows.Add(1, "佐藤", 30);
table.Rows.Add(2, "鈴木", 25);
この方法は短く書けるため便利です。
ただし、値の順番は列の順番と一致している必要があります。
C#table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
// Id, Name, Ageの順番で指定する
table.Rows.Add(1, "佐藤", 30);
列の順番を間違えると、型エラーや意図しないデータ登録の原因になります。初心者の場合は、まずNewRowで列名を指定する方法に慣れるとよいでしょう。
4-3. ループ処理で複数行を追加する
複数のデータをまとめて追加する場合は、ループ処理を使います。
C#DataTable table = new DataTable("Numbers");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Value", typeof(string));
for (int i = 1; i <= 5; i++)
{
table.Rows.Add(i, $"データ{i}");
}
実行結果を表示してみます。
C#foreach (DataRow row in table.Rows)
{
Console.WriteLine($"{row["Id"]}: {row["Value"]}");
}
1: データ1
2: データ2
3: データ3
4: データ4
5: データ5
データベースやファイルから取得したデータを1件ずつDataTableに追加する場合も、このようにループで処理します。
4-4. 配列やListからDataTableに変換して追加する
配列やList<T>の内容をDataTableに追加することもできます。
例えば、社員情報を表すクラスがあるとします。
C#class Employee
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}
List<Employee>からDataTableに変換する例です。
C#List<Employee> employees = new List<Employee>
{
new Employee { Id = 1, Name = "佐藤", Age = 30 },
new Employee { Id = 2, Name = "鈴木", Age = 25 },
new Employee { Id = 3, Name = "田中", Age = 35 }
};
DataTable table = new DataTable("Employees");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
foreach (Employee employee in employees)
{
table.Rows.Add(employee.Id, employee.Name, employee.Age);
}
配列の場合も同じ考え方です。
C#string[] names = { "佐藤", "鈴木", "田中" };
DataTable table = new DataTable("Names");
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
for (int i = 0; i < names.Length; i++)
{
table.Rows.Add(i + 1, names[i]);
}
4-5. 追加時によくある型エラーと対処法
DataTableにデータを追加するときによくあるのが型エラーです。
例えば、Age列をint型で定義しているのに、文字列を追加するとエラーになります。
C#DataTable table = new DataTable();
table.Columns.Add("Age", typeof(int));
// エラーになる可能性がある
table.Rows.Add("三十");
対処法は、列の型に合わせて値を変換することです。
C#string ageText = "30";
if (int.TryParse(ageText, out int age))
{
table.Rows.Add(age);
}
else
{
Console.WriteLine("年齢の形式が正しくありません。");
}
日付の場合も同様です。
C#string dateText = "2024/04/01";
if (DateTime.TryParse(dateText, out DateTime joinDate))
{
table.Rows.Add(joinDate);
}
else
{
Console.WriteLine("日付の形式が正しくありません。");
}
外部ファイルや画面入力から取得した値は、必ず正しい型に変換してからDataTableに追加するようにしましょう。
5. DataTableのデータを取得・表示する方法
5-1. foreachで全行を取得する
DataTableの全行を取得するには、Rowsをforeachでループします。
C#DataTable table = CreateEmployeeTable();
foreach (DataRow row in table.Rows)
{
Console.WriteLine($"{row["Id"]}: {row["Name"]}");
}
Rowsには、DataTableに含まれるすべての行が格納されています。
基本的な一覧表示では、この方法をよく使います。
5-2. 列名を指定して値を取得する
DataRowから値を取得するには、列名を指定します。
C#foreach (DataRow row in table.Rows)
{
int id = (int)row["Id"];
string name = (string)row["Name"];
int age = (int)row["Age"];
Console.WriteLine($"{id}: {name} ({age}歳)");
}
列名を指定する方法は、コードの意味がわかりやすいのがメリットです。
ただし、列名を間違えると実行時エラーになります。
C#// Name列が存在しない場合はエラー
Console.WriteLine(row["Name"]);
列名は文字列で直接書くのではなく、定数化しておくとミスを減らせます。
C#const string ColumnName = "Name";
Console.WriteLine(row[ColumnName]);
5-3. インデックス番号で値を取得する
列名ではなく、インデックス番号で値を取得することもできます。
C#foreach (DataRow row in table.Rows)
{
Console.WriteLine($"{row[0]}: {row[1]}");
}
インデックスは0から始まります。
例えば、列の順番がId、Name、Ageの場合、row[0]はId、row[1]はName、row[2]はAgeです。
ただし、インデックス番号での取得は、列の順番が変わると意味が変わってしまいます。可読性も下がるため、基本的には列名を指定する方法がおすすめです。
5-4. Rows.Countで行数を取得する
DataTableの行数を取得するには、Rows.Countを使います。
C#DataTable table = CreateEmployeeTable();
int rowCount = table.Rows.Count;
Console.WriteLine($"行数: {rowCount}");
検索結果があるかどうかを判定する場合にも使えます。
C#if (table.Rows.Count > 0)
{
Console.WriteLine("データがあります。");
}
else
{
Console.WriteLine("データがありません。");
}
5-5. Columns.Countで列数を取得する
列数を取得するには、Columns.Countを使います。
C#int columnCount = table.Columns.Count;
Console.WriteLine($"列数: {columnCount}");
すべての列名を表示する場合は、次のように書けます。
C#foreach (DataColumn column in table.Columns)
{
Console.WriteLine(column.ColumnName);
}
列構成を動的に確認したい場合に便利です。
5-6. DBNullを安全に判定する方法
DataTableでは、データが存在しない値はnullではなくDBNull.Valueで表されることがあります。
そのため、次のように直接キャストすると例外が発生する可能性があります。
C#// DepartmentがDBNullの場合、例外になる可能性がある
string department = (string)row["Department"];
安全に判定するには、DBNull.Valueかどうかを確認します。
C#if (row["Department"] != DBNull.Value)
{
string department = (string)row["Department"];
Console.WriteLine(department);
}
else
{
Console.WriteLine("部署は未設定です。");
}
IsNullメソッドを使うこともできます。
C#if (!row.IsNull("Department"))
{
Console.WriteLine(row["Department"]);
}
DBNullはDataTableで非常によく出てくるため、必ず覚えておきましょう。
6. DataTableからデータを検索・抽出する方法
6-1. Selectメソッドで条件検索する
DataTableから条件に一致する行を検索するには、Selectメソッドを使います。
C#DataTable table = CreateEmployeeTable();
DataRow[] rows = table.Select("Department = '開発部'");
foreach (DataRow row in rows)
{
Console.WriteLine($"{row["Name"]}: {row["Department"]}");
}
Selectメソッドの条件式は文字列で指定します。
SQLのWHERE句に似た感覚で書けますが、完全にSQLと同じではないため注意が必要です。
6-2. 複数条件で検索する
複数条件で検索する場合は、ANDやORを使います。
C#DataRow[] rows = table.Select("Department = '開発部' AND Age >= 25");
または、いずれかの条件に一致する行を検索する場合はORを使います。
C#DataRow[] rows = table.Select("Department = '開発部' OR Department = '営業部'");
条件式が複雑になる場合は、文字列の組み立てミスに注意しましょう。
C#string department = "開発部";
int minAge = 25;
DataRow[] rows = table.Select($"Department = '{department}' AND Age >= {minAge}");
文字列値はシングルクォートで囲む必要があります。
6-3. LIKEを使って部分一致検索する
部分一致検索をしたい場合は、LIKEを使います。
C#DataRow[] rows = table.Select("Name LIKE '佐%'");
この例では、名前が「佐」で始まる行を検索します。
任意の文字列を含む行を探したい場合は、次のように書きます。
C#DataRow[] rows = table.Select("Name LIKE '%田%'");
ただし、LIKEの書き方はSQLと似ていますが、環境や条件式によって使える構文に制限があります。
ユーザー入力を条件式に使う場合は、シングルクォートなどの特殊文字に注意してください。
C#string keyword = "田";
string escapedKeyword = keyword.Replace("'", "''");
DataRow[] rows = table.Select($"Name LIKE '%{escapedKeyword}%'");
6-4. 日付や数値を条件に検索する
数値条件の場合は、そのまま比較できます。
C#DataRow[] rows = table.Select("Age >= 30");
日付を条件にする場合は、日付リテラルの扱いに注意します。
C#DataRow[] rows = table.Select("JoinDate >= #2020-01-01#");
日付条件を文字列で組み立てる場合は、形式を統一すると安全です。
C#DateTime targetDate = new DateTime(2020, 1, 1);
string condition = $"JoinDate >= #{targetDate:yyyy-MM-dd}#";
DataRow[] rows = table.Select(condition);
日付や数値の検索では、列のデータ型が正しく設定されていることが重要です。日付を文字列型で持っていると、期待通りに比較できない場合があります。
6-5. Findメソッドで主キー検索する
主キーを設定している場合は、Rows.Findで検索できます。
C#DataTable table = new DataTable("Employees");
DataColumn idColumn = table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.PrimaryKey = new DataColumn[] { idColumn };
table.Rows.Add(1, "佐藤");
table.Rows.Add(2, "鈴木");
DataRow? row = table.Rows.Find(2);
if (row != null)
{
Console.WriteLine(row["Name"]);
}
Findは主キーを使って検索するため、特定のIDから1件を取得したい場合に便利です。
複数列を主キーにすることもできます。
C#table.PrimaryKey = new DataColumn[]
{
table.Columns["Id"],
table.Columns["Department"]
};
複合主キーで検索する場合は、値を配列で指定します。
C#DataRow? row = table.Rows.Find(new object[] { 1, "開発部" });
6-6. DataViewのRowFilterで絞り込む
DataViewを使うと、DataTableに対して絞り込みや並び替えを行えます。
C#DataTable table = CreateEmployeeTable();
DataView view = new DataView(table);
view.RowFilter = "Department = '開発部'";
foreach (DataRowView rowView in view)
{
Console.WriteLine(rowView["Name"]);
}
DataViewは、元のDataTableをビューとして表示する仕組みです。検索結果を画面に表示したい場合や、フィルター条件を変更しながら一覧を更新したい場合に便利です。
DataGridViewなどの画面部品にバインドする場合にもよく使われます。
6-7. 検索結果が0件の場合の処理
Selectメソッドの検索結果はDataRow[]で返されます。
検索結果が0件の場合は、配列の長さが0になります。
C#DataRow[] rows = table.Select("Department = '人事部'");
if (rows.Length == 0)
{
Console.WriteLine("該当するデータはありません。");
}
else
{
foreach (DataRow row in rows)
{
Console.WriteLine(row["Name"]);
}
}
検索結果が必ず存在する前提で処理を書くと、後続処理で例外や意図しない動作が発生することがあります。
検索や抽出では、0件の場合の処理を必ず考えておきましょう。
7. DataTableを並び替える方法
7-1. Selectメソッドの第2引数でソートする
DataTable.Selectメソッドでは、第2引数に並び替え条件を指定できます。
C#DataTable table = CreateEmployeeTable();
DataRow[] rows = table.Select("", "Age ASC");
foreach (DataRow row in rows)
{
Console.WriteLine($"{row["Name"]}: {row["Age"]}");
}
第1引数は検索条件、第2引数はソート条件です。
すべての行を対象に並び替えたい場合は、第1引数に空文字を指定します。
C#DataRow[] rows = table.Select("", "Age DESC");
7-2. DataView.Sortで並び替える
DataViewを使って並び替えることもできます。
C#DataTable table = CreateEmployeeTable();
DataView view = new DataView(table);
view.Sort = "Age ASC";
foreach (DataRowView rowView in view)
{
Console.WriteLine($"{rowView["Name"]}: {rowView["Age"]}");
}
DataView.Sortは、画面表示用に並び替えたい場合に便利です。
元のDataTableの行順を直接変更するのではなく、並び替えたビューとして扱います。
7-3. 昇順・降順を指定する
昇順はASC、降順はDESCで指定します。
C#// 年齢の昇順
view.Sort = "Age ASC";
// 年齢の降順
view.Sort = "Age DESC";
文字列列でも同じように指定できます。
C#view.Sort = "Name ASC";
ASCを省略すると昇順として扱われます。
C#view.Sort = "Age";
明示的に書いた方が読みやすいため、実務ではASCまたはDESCを付けることをおすすめします。
7-4. 複数列で並び替える
複数列で並び替える場合は、カンマ区切りで指定します。
C#view.Sort = "Department ASC, Age DESC";
この例では、まず部署名の昇順で並び替え、同じ部署内では年齢の降順で並び替えます。
Selectメソッドでも同じように指定できます。
C#DataRow[] rows = table.Select("", "Department ASC, Age DESC");
複数列ソートは、一覧表示や帳票出力でよく使います。
7-5. 並び替えた結果をDataTableに戻す
DataViewで並び替えた結果を新しいDataTableに変換するには、ToTableを使います。
C#DataTable table = CreateEmployeeTable();
DataView view = new DataView(table);
view.Sort = "Age ASC";
DataTable sortedTable = view.ToTable();
これで、並び替え後の内容を持つ新しいDataTableを作成できます。
元のDataTableを変更せず、並び替え後のデータだけを別で扱いたい場合に便利です。
7-6. 日本語・文字列・数値の並び替えで注意すること
並び替えでは、列のデータ型に注意が必要です。
数値を文字列型で保持している場合、期待通りの順番にならないことがあります。
例えば、文字列として"1"、"10"、"2"を並び替えると、数値順ではなく文字列順になるため、1、10、2のような順番になる場合があります。
数値として並び替えたい列は、必ずintやdecimalなどの数値型で定義しましょう。
C#table.Columns.Add("Price", typeof(decimal));
日本語の文字列を並び替える場合は、環境のカルチャや文字コード、比較ルールによって順番が期待と異なる場合があります。ふりがな順で並び替えたい場合は、ふりがな用の列を別に用意して、その列でソートする方法が現実的です。
8. DataTableのデータを更新・削除する方法
8-1. 指定した行の値を更新する
DataTableの行を更新するには、対象のDataRowを取得して値を変更します。
C#DataTable table = CreateEmployeeTable();
DataRow row = table.Rows[0];
row["Name"] = "佐藤一郎";
row["Age"] = 31;
列名を指定して値を代入するだけで更新できます。
更新後の内容を確認します。
C#Console.WriteLine($"{row["Name"]}: {row["Age"]}");
8-2. 条件に一致する行だけ更新する
条件に一致する行を更新したい場合は、Selectで検索してから値を変更します。
C#DataRow[] rows = table.Select("Department = '開発部'");
foreach (DataRow row in rows)
{
row["Department"] = "システム開発部";
}
特定のIDの行だけ更新する場合は、次のように書けます。
C#DataRow[] rows = table.Select("Id = 2");
if (rows.Length > 0)
{
rows[0]["Name"] = "鈴木次郎";
}
主キーを設定している場合は、Rows.Findを使うと便利です。
C#DataRow? row = table.Rows.Find(2);
if (row != null)
{
row["Name"] = "鈴木次郎";
}
8-3. Rows.Removeで行を削除する
行を完全に削除するには、Rows.Removeを使います。
C#DataRow row = table.Rows[0];
table.Rows.Remove(row);
条件に一致する行を削除する場合は、次のように書けます。
C#DataRow[] rows = table.Select("Department = '総務部'");
foreach (DataRow row in rows)
{
table.Rows.Remove(row);
}
ただし、Rowsを直接ループしながら削除するとエラーになる場合があります。
避けるべき例です。
C#// ループ中に削除すると問題になることがある
foreach (DataRow row in table.Rows)
{
table.Rows.Remove(row);
}
削除対象を先に配列として取得してから削除するのが安全です。
8-4. Deleteメソッドで行を削除状態にする
Deleteメソッドを使うと、行を削除状態にできます。
C#DataRow row = table.Rows[0];
row.Delete();
Rows.Removeは行をコレクションから取り除きますが、Deleteは行の状態を削除に変更します。
データベース更新と連携する場合、Deleteで削除状態にしてからDataAdapterなどで反映することがあります。
削除状態の行にアクセスすると例外が発生することがあるため、RowStateを確認する場合があります。
C#foreach (DataRow row in table.Rows)
{
if (row.RowState != DataRowState.Deleted)
{
Console.WriteLine(row["Name"]);
}
}
8-5. AcceptChangesとRejectChangesの使い方
AcceptChangesは、現在の変更内容を確定します。
C#table.AcceptChanges();
RejectChangesは、変更内容を取り消して元に戻します。
C#table.RejectChanges();
例えば、行を更新したあとにRejectChangesを呼ぶと、更新前の状態に戻ります。
C#DataTable table = CreateEmployeeTable();
table.AcceptChanges();
table.Rows[0]["Name"] = "変更後";
Console.WriteLine(table.Rows[0]["Name"]);
table.RejectChanges();
Console.WriteLine(table.Rows[0]["Name"]);
AcceptChangesを呼ぶと変更前の情報が確定されるため、その後はRejectChangesで元に戻せなくなります。
データベース更新処理と組み合わせる場合は、AcceptChangesを呼ぶタイミングに注意しましょう。
8-6. Clearで全データを削除する
DataTableの全行を削除するには、Clearを使います。
C#table.Clear();
Clearは行データをすべて削除しますが、列定義は残ります。
C#Console.WriteLine(table.Rows.Count);
Console.WriteLine(table.Columns.Count);
行だけを消して、同じ列構成で新しいデータを入れ直したい場合に便利です。
9. LINQを使ってDataTableを操作する方法
9-1. AsEnumerableを使うための準備
DataTableをLINQで操作するには、AsEnumerableを使います。
次の名前空間を追加します。
C#using System.Data;
using System.Linq;
環境によっては、System.Data.DataSetExtensionsが必要になる場合があります。
基本的な書き方は次の通りです。
C#DataTable table = CreateEmployeeTable();
var rows = table.AsEnumerable();
AsEnumerableを使うことで、Where、OrderBy、SelectなどのLINQメソッドを使えるようになります。
9-2. LINQのWhereで条件検索する
LINQで条件検索するには、Whereを使います。
C#var rows = table.AsEnumerable()
.Where(row => row.Field<string>("Department") == "開発部");
foreach (DataRow row in rows)
{
Console.WriteLine(row.Field<string>("Name"));
}
Selectメソッドの条件式は文字列で書きますが、LINQではラムダ式で条件を書けます。
数値条件も自然に書けます。
C#var rows = table.AsEnumerable()
.Where(row => row.Field<int>("Age") >= 30);
C#らしい書き方で検索したい場合は、LINQが便利です。
9-3. LINQのOrderByで並び替える
LINQで並び替えるには、OrderByやOrderByDescendingを使います。
C#var rows = table.AsEnumerable()
.OrderBy(row => row.Field<int>("Age"));
降順にする場合は、OrderByDescendingを使います。
C#var rows = table.AsEnumerable()
.OrderByDescending(row => row.Field<int>("Age"));
複数列で並び替える場合は、ThenByやThenByDescendingを使います。
C#var rows = table.AsEnumerable()
.OrderBy(row => row.Field<string>("Department"))
.ThenByDescending(row => row.Field<int>("Age"));
9-4. LINQのSelectで必要な列だけ取得する
LINQのSelectを使うと、必要な列だけを取り出せます。
C#var names = table.AsEnumerable()
.Select(row => row.Field<string>("Name"));
foreach (string? name in names)
{
Console.WriteLine(name);
}
匿名型として複数列を取得することもできます。
C#var employees = table.AsEnumerable()
.Select(row => new
{
Id = row.Field<int>("Id"),
Name = row.Field<string>("Name"),
Department = row.Field<string>("Department")
});
foreach (var employee in employees)
{
Console.WriteLine($"{employee.Id}: {employee.Name} - {employee.Department}");
}
DataTableから画面表示や集計に必要な列だけを取り出す場合に便利です。
9-5. Fieldメソッドで型安全に値を取得する
Field<T>メソッドを使うと、DataRowの値を指定した型で取得できます。
C#int age = row.Field<int>("Age");
string? name = row.Field<string>("Name");
DateTime joinDate = row.Field<DateTime>("JoinDate");
Field<T>はDBNullを扱う場合にも便利です。
NULLを許可する列では、nullable型を使えます。
C#int? age = row.Field<int?>("Age");
DateTime? joinDate = row.Field<DateTime?>("JoinDate");
通常のキャストよりも安全に書けるため、LINQでDataTableを扱う場合はField<T>を使うのがおすすめです。
9-6. CopyToDataTableでLINQの結果をDataTableに戻す
LINQで絞り込んだ結果をDataTableに戻すには、CopyToDataTableを使います。
C#var query = table.AsEnumerable()
.Where(row => row.Field<string>("Department") == "開発部");
DataTable resultTable = query.CopyToDataTable();
ただし、検索結果が0件の場合にCopyToDataTableを呼ぶと例外が発生します。
安全に処理するには、Anyで確認します。
C#var query = table.AsEnumerable()
.Where(row => row.Field<string>("Department") == "人事部");
DataTable resultTable;
if (query.Any())
{
resultTable = query.CopyToDataTable();
}
else
{
resultTable = table.Clone();
}
Cloneは、列構成だけをコピーした空のDataTableを作成します。
9-7. LINQで集計・グループ化する
LINQを使うと、DataTableのデータを集計できます。
部署ごとの人数を集計する例です。
C#var groups = table.AsEnumerable()
.GroupBy(row => row.Field<string>("Department"))
.Select(group => new
{
Department = group.Key,
Count = group.Count()
});
foreach (var group in groups)
{
Console.WriteLine($"{group.Department}: {group.Count}人");
}
年齢の平均を求めることもできます。
C#double averageAge = table.AsEnumerable()
.Average(row => row.Field<int>("Age"));
Console.WriteLine($"平均年齢: {averageAge}");
最大値や最小値も簡単に取得できます。
C#int maxAge = table.AsEnumerable()
.Max(row => row.Field<int>("Age"));
int minAge = table.AsEnumerable()
.Min(row => row.Field<int>("Age"));
集計処理では、SelectメソッドよりもLINQの方が読みやすく書けることが多いです。
9-8. SelectメソッドとLINQの使い分け
DataTable.SelectとLINQは、どちらも検索や抽出に使えます。
Selectメソッドは、短い条件式で手軽に検索したい場合に便利です。
C#DataRow[] rows = table.Select("Age >= 30");
一方、LINQは、C#の文法で型安全に処理を書きたい場合や、複雑な条件、集計、グループ化を行いたい場合に向いています。
C#var rows = table.AsEnumerable()
.Where(row => row.Field<int>("Age") >= 30)
.OrderBy(row => row.Field<string>("Name"));
初心者はまずSelectで基本的な検索に慣れ、その後LINQを使えるようになると、より柔軟なデータ操作ができるようになります。
10. DataTableとデータベース・CSV・画面表示の連携
10-1. SQLの取得結果をDataTableに格納する
データベースから取得した結果をDataTableに格納する場面は非常に多いです。
例えば、SQL Serverからデータを取得する場合、SqlDataAdapterを使ってDataTableに読み込むことができます。
C#using System.Data;
using Microsoft.Data.SqlClient;
string connectionString = "接続文字列";
string sql = "SELECT Id, Name, Age FROM Employees";
DataTable table = new DataTable();
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlDataAdapter adapter = new SqlDataAdapter(sql, connection))
{
adapter.Fill(table);
}
Fillを呼ぶと、SQLの検索結果がDataTableに格納されます。
データベースの種類によって使用する接続クラスは異なりますが、考え方は同じです。
10-2. DataAdapterでDataTableにデータを読み込む
DataAdapterは、データベースとDataTableの間をつなぐ役割を持ちます。
C#DataTable table = new DataTable();
using (SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Employees", connectionString))
{
adapter.Fill(table);
}
DataAdapterを使うと、SQLの結果を簡単にDataTableへ読み込めます。
また、設定によってはDataTableの変更内容をデータベースへ反映することもできます。
ただし、現在の開発では、Entity FrameworkやDapperなどを使うケースも多くなっています。既存システムや業務アプリではDataTableとDataAdapterの組み合わせもよく使われるため、基本は理解しておくと役立ちます。
10-3. DataTableをDataGridViewに表示する
Windows Formsで一覧表示を行う場合、DataGridViewにDataTableを設定できます。
C#DataTable table = CreateEmployeeTable();
dataGridView1.DataSource = table;
これだけで、DataTableの内容が表形式で表示されます。
絞り込みや並び替えをしたい場合は、DataViewを使って表示できます。
C#DataView view = new DataView(table);
view.RowFilter = "Department = '開発部'";
view.Sort = "Age DESC";
dataGridView1.DataSource = view;
DataTableは画面表示との相性がよいため、Windows Formsの業務アプリでよく使われます。
10-4. DataTableをCSVに出力する
DataTableをCSVに出力するには、列名と行データを文字列として書き出します。
C#using System.Text;
static void ExportToCsv(DataTable table, string filePath)
{
StringBuilder sb = new StringBuilder();
string[] columnNames = table.Columns
.Cast<DataColumn>()
.Select(column => EscapeCsv(column.ColumnName))
.ToArray();
sb.AppendLine(string.Join(",", columnNames));
foreach (DataRow row in table.Rows)
{
string[] fields = table.Columns
.Cast<DataColumn>()
.Select(column => EscapeCsv(row[column]?.ToString() ?? ""))
.ToArray();
sb.AppendLine(string.Join(",", fields));
}
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
static string EscapeCsv(string value)
{
if (value.Contains(",") || value.Contains("\"") || value.Contains("\n"))
{
value = value.Replace("\"", "\"\"");
return $"\"{value}\"";
}
return value;
}
CSVでは、カンマ、ダブルクォート、改行を含む値に注意が必要です。単純にstring.Joinするだけでは、正しいCSVにならない場合があります。
10-5. CSVを読み込んでDataTableに変換する
CSVを読み込んでDataTableに変換する簡単な例です。
C#static DataTable ReadCsv(string filePath)
{
DataTable table = new DataTable();
string[] lines = File.ReadAllLines(filePath);
if (lines.Length == 0)
{
return table;
}
string[] headers = lines[0].Split(',');
foreach (string header in headers)
{
table.Columns.Add(header, typeof(string));
}
for (int i = 1; i < lines.Length; i++)
{
string[] values = lines[i].Split(',');
table.Rows.Add(values);
}
return table;
}
ただし、この例は簡易的な実装です。値にカンマやダブルクォート、改行が含まれるCSVには対応していません。
実務でCSVを扱う場合は、CSV専用ライブラリを利用するか、CSV仕様に対応した読み込み処理を実装することをおすすめします。
10-6. DataTableをJSONに変換する
DataTableをJSONに変換したい場合は、行ごとにDictionaryへ変換してからシリアライズする方法があります。
C#using System.Text.Json;
static string DataTableToJson(DataTable table)
{
var rows = new List<Dictionary<string, object?>>();
foreach (DataRow row in table.Rows)
{
var dict = new Dictionary<string, object?>();
foreach (DataColumn column in table.Columns)
{
object? value = row[column];
if (value == DBNull.Value)
{
value = null;
}
dict[column.ColumnName] = value;
}
rows.Add(dict);
}
return JsonSerializer.Serialize(rows, new JsonSerializerOptions
{
WriteIndented = true
});
}
使用例です。
C#DataTable table = CreateEmployeeTable();
string json = DataTableToJson(table);
Console.WriteLine(json);
Web APIや外部システム連携ではJSON形式がよく使われるため、DataTableからJSONへ変換する方法も覚えておくと便利です。
11. DataTableでよくあるエラーと解決方法
11-1. 列が存在しないエラーの原因と対処法
存在しない列名を指定すると、エラーが発生します。
C#Console.WriteLine(row["FullName"]);
FullName列が存在しない場合、例外になります。
対処法は、列が存在するか確認してからアクセスすることです。
C#if (table.Columns.Contains("FullName"))
{
Console.WriteLine(row["FullName"]);
}
else
{
Console.WriteLine("FullName列は存在しません。");
}
列名の入力ミスを防ぐには、列名を定数化する方法も有効です。
C#public static class EmployeeColumns
{
public const string Id = "Id";
public const string Name = "Name";
public const string Age = "Age";
}
C#Console.WriteLine(row[EmployeeColumns.Name]);
11-2. 型変換エラーの原因と対処法
型変換エラーは、列の型と値の型が一致していない場合に発生します。
C#int age = (int)row["Age"];
Age列が文字列だった場合や、DBNullだった場合は例外になることがあります。
安全に変換するには、型を確認します。
C#if (row["Age"] != DBNull.Value && int.TryParse(row["Age"].ToString(), out int age))
{
Console.WriteLine(age);
}
else
{
Console.WriteLine("年齢を取得できません。");
}
LINQを使う場合は、Field<T>を利用すると読みやすくなります。
C#int? age = row.Field<int?>("Age");
列定義時点で正しい型を指定することも重要です。
11-3. DBNullで例外が発生する場合の対処法
DataTableでは、値が未設定の場合にDBNull.Valueが使われることがあります。
次のようなコードは、DBNullが入っていると例外になる可能性があります。
C#string department = row["Department"].ToString();
ToString自体は空文字になる場合がありますが、型変換では問題が起きやすくなります。
安全に扱うには、IsNullを使います。
C#string department = row.IsNull("Department")
? "未設定"
: row["Department"].ToString()!;
または、LINQのField<T>でnullableとして取得します。
C#string? department = row.Field<string?>("Department");
DBNullとnullは別物であることを理解しておきましょう。
11-4. CopyToDataTableでデータが0件のときの対処法
CopyToDataTableは、対象の行が0件の場合に例外が発生します。
次のコードは、検索結果が0件だとエラーになります。
C#DataTable result = table.AsEnumerable()
.Where(row => row.Field<string>("Department") == "人事部")
.CopyToDataTable();
対処法は、Anyで確認することです。
C#var query = table.AsEnumerable()
.Where(row => row.Field<string>("Department") == "人事部");
DataTable result;
if (query.Any())
{
result = query.CopyToDataTable();
}
else
{
result = table.Clone();
}
table.Clone()を使うと、列構成だけ同じ空のDataTableを作成できます。
11-5. Selectの条件式でエラーになる場合の対処法
DataTable.Selectの条件式は文字列で指定するため、書き方を間違えるとエラーになります。
よくある原因は、文字列をシングルクォートで囲んでいないことです。
C#// エラーになる
DataRow[] rows = table.Select("Department = 開発部");
正しくは次のように書きます。
C#DataRow[] rows = table.Select("Department = '開発部'");
文字列内にシングルクォートが含まれる場合は、エスケープが必要です。
C#string keyword = "O'Brien";
string escaped = keyword.Replace("'", "''");
DataRow[] rows = table.Select($"Name = '{escaped}'");
日付条件では、#で囲む書き方を使う場合があります。
C#DataRow[] rows = table.Select("JoinDate >= #2020-01-01#");
条件式が複雑になりすぎる場合は、LINQを使った方が読みやすく安全です。
11-6. 大量データで処理が遅い場合の改善策
DataTableで大量データを扱うと、処理が遅くなることがあります。
改善策として、まず不要な列や行を持たないようにします。データベースから取得する時点で必要な列だけをSELECTすることが大切です。
SQLSELECT Id, Name, Age FROM Employees
すべての列を取得するSELECT *は便利ですが、大量データでは避けた方がよい場合があります。
検索を頻繁に行う場合は、主キーを設定してFindを使う方法もあります。
C#table.PrimaryKey = new DataColumn[] { table.Columns["Id"] };
DataRow? row = table.Rows.Find(100);
大量の追加処理を行う場合は、BeginLoadDataとEndLoadDataを使うことでパフォーマンスが改善する場合があります。
C#table.BeginLoadData();
for (int i = 0; i < 100000; i++)
{
table.Rows.Add(i, $"Name{i}");
}
table.EndLoadData();
ただし、非常に大量のデータを扱う場合は、DataTableではなくデータベース側で処理したり、List<T>やストリーミング処理を検討したりすることも重要です。
12. DataTableを使うときの実践的なポイント
12-1. 列名は定数化して管理する
DataTableでは列名を文字列で指定することが多いため、タイプミスが起きやすいです。
C#row["Name"]
row["Nmae"]
このようなミスはコンパイル時には検出されず、実行時にエラーになります。
対策として、列名を定数化しましょう。
C#public static class EmployeeColumns
{
public const string Id = "Id";
public const string Name = "Name";
public const string Age = "Age";
public const string Department = "Department";
}
使用時は次のように書きます。
C#row[EmployeeColumns.Name] = "佐藤";
列名を1か所で管理することで、修正漏れや入力ミスを防ぎやすくなります。
12-2. 型を明確にして予期しないエラーを防ぐ
DataTableを使うときは、列のデータ型を明確に指定しましょう。
C#table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
table.Columns.Add("JoinDate", typeof(DateTime));
すべてを文字列として扱うと、後で検索や並び替えをするときに問題が起きることがあります。
例えば、数値を文字列で持っていると、数値順ではなく文字列順で並び替えられる場合があります。
日付も同様に、文字列ではなくDateTime型で持つことで、正しい日付比較ができます。
データ型を明確にすることは、DataTableを安全に使う基本です。
12-3. 大量データではDataTable以外の選択肢も検討する
DataTableは便利ですが、大量データを扱う場合には注意が必要です。
すべてのデータをメモリ上に保持するため、データ量が多いとメモリ使用量が増えます。また、検索や並び替えの処理が重くなることもあります。
大量データを扱う場合は、次のような方法も検討しましょう。
データベースで検索、並び替え、集計を行ってから必要な結果だけを取得する方法があります。SQLのWHERE、ORDER BY、GROUP BYを活用すると、アプリケーション側の負荷を減らせます。
アプリケーション内で型安全に処理したい場合は、List<T>を使う方法もあります。
大量ファイルを処理する場合は、すべてをDataTableに読み込まず、1行ずつ処理するストリーミング方式も有効です。
DataTableは便利な選択肢ですが、常に最適とは限りません。
12-4. 検索・並び替えは用途に応じて方法を選ぶ
DataTableの検索や並び替えには、複数の方法があります。
簡単な条件検索ならSelectメソッドが使えます。
C#DataRow[] rows = table.Select("Age >= 30");
画面表示と組み合わせて絞り込みや並び替えをしたい場合は、DataViewが便利です。
C#DataView view = new DataView(table);
view.RowFilter = "Department = '開発部'";
view.Sort = "Age DESC";
C#らしく柔軟に処理したい場合は、LINQが向いています。
C#var rows = table.AsEnumerable()
.Where(row => row.Field<int>("Age") >= 30)
.OrderBy(row => row.Field<string>("Name"));
それぞれの方法に特徴があるため、用途に応じて使い分けましょう。
12-5. 初心者が覚えるべきDataTable操作の優先順位
初心者がまず覚えるべきDataTable操作は、次の順番です。
最初に、DataTableの作成と列の追加を覚えましょう。
C#DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
次に、行の追加を覚えます。
C#table.Rows.Add(1, "佐藤");
その次に、foreachでデータを取得する方法を覚えます。
C#foreach (DataRow row in table.Rows)
{
Console.WriteLine(row["Name"]);
}
続いて、Selectによる検索、DataViewによる並び替え、更新、削除を学ぶとよいでしょう。
最後に、LINQやCSV、データベース連携を学ぶと、実務で使える範囲が大きく広がります。
13. C# DataTableに関するよくある質問
13-1. DataTableは現在でも使われていますか?
はい、DataTableは現在でも使われています。
特に、既存の業務システム、Windows Formsアプリ、データベース連携、CSV処理、帳票出力などでは今でもよく登場します。
一方で、新規開発ではList<T>、Entity Framework、Dapper、DTOクラスなどを使って型安全にデータを扱う設計も増えています。
つまり、DataTableは古い技術というより、用途によって今でも有効な選択肢です。既存システムの保守や表形式データの加工では、覚えておく価値があります。
13-2. DataTableとListはどちらを使うべきですか?
用途によって使い分けます。
表形式のデータを動的に扱いたい場合や、データベース、CSV、DataGridViewなどと連携したい場合はDataTableが便利です。
一方、アプリケーション内部で型安全にデータを扱いたい場合は、List<T>がおすすめです。
例えば、社員情報を扱うなら次のようにクラスを定義してList<Employee>で管理できます。
C#class Employee
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}
C#List<Employee> employees = new List<Employee>();
列構成が固定で、C#のコード内で扱うデータならList<T>、表形式で柔軟に扱う必要があるならDataTableと考えるとわかりやすいです。
13-3. DataTableで特定の列だけ取得できますか?
はい、取得できます。
LINQを使うと、特定の列だけを簡単に取得できます。
C#var names = table.AsEnumerable()
.Select(row => row.Field<string>("Name"));
foreach (string? name in names)
{
Console.WriteLine(name);
}
複数列を取得する場合は、匿名型を使います。
C#var result = table.AsEnumerable()
.Select(row => new
{
Id = row.Field<int>("Id"),
Name = row.Field<string>("Name")
});
また、DataView.ToTableを使って特定の列だけを持つDataTableを作ることもできます。
C#DataView view = new DataView(table);
DataTable selectedTable = view.ToTable(false, "Id", "Name");
13-4. DataTableで重複行を削除できますか?
はい、削除できます。
特定の列を基準に重複を取り除く場合は、DataView.ToTableを使う方法があります。
C#DataView view = new DataView(table);
DataTable distinctTable = view.ToTable(true, "Department");
第1引数にtrueを指定すると、重複を除外します。
複数列を基準にすることもできます。
C#DataTable distinctTable = view.ToTable(true, "Department", "Age");
LINQで重複を除外する方法もあります。
C#var departments = table.AsEnumerable()
.Select(row => row.Field<string>("Department"))
.Distinct();
どの列を基準に重複とみなすかを明確にすることが重要です。
13-5. DataTableを高速化する方法はありますか?
いくつかの方法があります。
大量データを追加する場合は、BeginLoadDataとEndLoadDataを使うことで改善する場合があります。
C#table.BeginLoadData();
for (int i = 0; i < 100000; i++)
{
table.Rows.Add(i, $"Name{i}");
}
table.EndLoadData();
主キー検索を行う場合は、PrimaryKeyを設定してRows.Findを使うと効率的です。
C#table.PrimaryKey = new DataColumn[] { table.Columns["Id"] };
DataRow? row = table.Rows.Find(100);
また、不要な列や行を持たないようにすることも大切です。
データベースから取得する場合は、必要なデータだけをSQLで絞り込んでからDataTableに読み込むと、メモリ使用量と処理時間を減らせます。
ただし、極端に大量のデータを扱う場合は、DataTable以外の方法も検討しましょう。
13-6. DataTableをLINQで使うには何が必要ですか?
DataTableをLINQで使うには、AsEnumerableを使います。
必要な名前空間は主に次の通りです。
C#using System.Data;
using System.Linq;
環境によっては、System.Data.DataSetExtensionsが必要になる場合があります。
基本的なLINQの例です。
C#var rows = table.AsEnumerable()
.Where(row => row.Field<int>("Age") >= 30);
foreach (DataRow row in rows)
{
Console.WriteLine(row.Field<string>("Name"));
}
Field<T>を使うと、型を指定して値を取得できるため、安全で読みやすいコードになります。
まとめ
C#のDataTableは、表形式のデータをメモリ上で扱うための便利なクラスです。列をDataColumn、行をDataRowとして管理し、データの追加、取得、検索、並び替え、更新、削除などを行えます。
基本的な使い方は、DataTableを作成し、Columns.Addで列を追加し、Rows.AddやNewRowで行を追加する流れです。
検索にはSelectメソッド、主キー検索にはRows.Find、画面表示や絞り込みにはDataView、柔軟な条件検索や集計にはLINQが便利です。
また、DataTableはデータベース、CSV、DataGridView、JSONなどとの連携にもよく使われます。業務アプリケーションでは今でも利用される場面が多いため、基本操作を理解しておくと実務で役立ちます。
一方で、DataTableは列名を文字列で指定するため、タイプミスや型変換エラー、DBNullの扱いに注意が必要です。列名の定数化、データ型の明確化、NULLチェックを意識して使うことが大切です。
大量データや型安全性が重要な場面では、List<T>やデータベース側の処理も検討しましょう。用途に応じてDataTableを正しく使い分けることで、C#でのデータ処理をより効率的に行えるようになります。

