C# DataTableの使い方完全ガイド|作成・追加・検索・並び替え・LINQまで初心者向けに解説

はじめに

C#で表形式のデータを扱うときによく使われるのがDataTableです。DataTableは、行と列で構成されたデータをメモリ上で管理できるクラスで、データベースの検索結果、CSVデータ、画面表示用の一覧データなどを扱う場面でよく利用されます。

C#を学び始めたばかりの方にとって、DataTableは少し難しく感じるかもしれません。DataRowDataColumnDataSetDataViewなど関連するクラスも多く、どれをどの場面で使えばよいのか迷いやすいからです。

この記事では、C#のDataTableについて、作成、列の追加、データの追加、取得、検索、並び替え、更新、削除、LINQでの操作、CSVやデータベースとの連携まで、初心者にもわかりやすく解説します。

基本的な使い方から実践的な注意点まで順番に説明するので、c# datatableの使い方を一通り理解したい方は、ぜひ参考にしてください。

1. C#のDataTableとは?初心者向けに役割と使いどころを解説

1-1. DataTableとは表形式のデータを扱うためのクラス

DataTableとは、C#で表形式のデータをメモリ上に保持するためのクラスです。

表形式のデータとは、Excelやデータベースのテーブルのように、列と行で構成されたデータのことです。

例えば、次のような社員一覧を考えてみましょう。

IdNameAgeDepartment
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;

これにより、DataTableDataRowDataColumnDataSetDataViewなどのクラスを使えるようになります。

基本的なコードは次のようになります。

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が必要になることがあります。AsEnumerableField<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. サンプルで使用するデータ構成

この記事では、主に社員情報を例にして解説します。

使用する列は次の通りです。

列名内容
Idint社員ID
Namestring氏名
Ageint年齢
Departmentstring部署
JoinDateDateTime入社日

サンプルの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に行を追加する基本的な方法は、NewRowDataRowを作成し、値を設定してから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の全行を取得するには、Rowsforeachでループします。

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から始まります。

例えば、列の順番がIdNameAgeの場合、row[0]Idrow[1]Namerow[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"]);
}

DBNullDataTableで非常によく出てくるため、必ず覚えておきましょう。

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. 複数条件で検索する

複数条件で検索する場合は、ANDORを使います。

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"を並び替えると、数値順ではなく文字列順になるため、1102のような順番になる場合があります。

数値として並び替えたい列は、必ずintdecimalなどの数値型で定義しましょう。

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を使うことで、WhereOrderBySelectなどの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で並び替えるには、OrderByOrderByDescendingを使います。

C#
var rows = table.AsEnumerable()
.OrderBy(row => row.Field<int>("Age"));

降順にする場合は、OrderByDescendingを使います。

C#
var rows = table.AsEnumerable()
.OrderByDescending(row => row.Field<int>("Age"));

複数列で並び替える場合は、ThenByThenByDescendingを使います。

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などを使うケースも多くなっています。既存システムや業務アプリではDataTableDataAdapterの組み合わせもよく使われるため、基本は理解しておくと役立ちます。

10-3. DataTableをDataGridViewに表示する

Windows Formsで一覧表示を行う場合、DataGridViewDataTableを設定できます。

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");

DBNullnullは別物であることを理解しておきましょう。

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することが大切です。

SQL
SELECT Id, Name, Age FROM Employees

すべての列を取得するSELECT *は便利ですが、大量データでは避けた方がよい場合があります。

検索を頻繁に行う場合は、主キーを設定してFindを使う方法もあります。

C#
table.PrimaryKey = new DataColumn[] { table.Columns["Id"] };
DataRow? row = table.Rows.Find(100);

大量の追加処理を行う場合は、BeginLoadDataEndLoadDataを使うことでパフォーマンスが改善する場合があります。

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のWHEREORDER BYGROUP 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を高速化する方法はありますか?

いくつかの方法があります。

大量データを追加する場合は、BeginLoadDataEndLoadDataを使うことで改善する場合があります。

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.AddNewRowで行を追加する流れです。

検索にはSelectメソッド、主キー検索にはRows.Find、画面表示や絞り込みにはDataView、柔軟な条件検索や集計にはLINQが便利です。

また、DataTableはデータベース、CSV、DataGridView、JSONなどとの連携にもよく使われます。業務アプリケーションでは今でも利用される場面が多いため、基本操作を理解しておくと実務で役立ちます。

一方で、DataTableは列名を文字列で指定するため、タイプミスや型変換エラー、DBNullの扱いに注意が必要です。列名の定数化、データ型の明確化、NULLチェックを意識して使うことが大切です。

大量データや型安全性が重要な場面では、List<T>やデータベース側の処理も検討しましょう。用途に応じてDataTableを正しく使い分けることで、C#でのデータ処理をより効率的に行えるようになります。