データテーブル
最終更新:
atachi
.NET Frameworksバージョン1時代から実装されている、メモリ上で稼働する簡易的なオンメモリデータベースです。
データテーブルを理解しうまく使えるかどうかが、アプリケーション設計において非常に重要な要素となります。
データテーブルにはレコードの追加位置や削除されたレコードを記録されるため、後からそれらの情報を引き出すことができます。
また、ビュー機能を使うことで、任意の条件だけを含んだデータセットを抽出することができます。
DataTableの欠点としては、テーブルのフィールドに型の概念を持ち込めないことです。
スキームの指定時に格納可能なデータの型を設定できますが、これはDataTable内でのみ使用される内部情報(検索や集計、ソートなどの操作で使われる)だけです。
あるレコードのフィールドへのアクセスにはフィールド名を文字列で指定する必要があるので、タイプミスの問題や取得したデータはすべてobject型であり、プログラマが自分で型を調べキャストしなければならない。
使い方
// DataTableを作成
// - 強い型付けでカラムを定義
// - FirstNameカラムはstring型
// - LastNameカラムはstring型
// - AgeカラムはInt32型
DataTable table = new DataTable();
table.Columns.Add("FirstName", typeof(string));
table.Columns.Add("LastName", typeof(string));
table.Columns.Add("Age", typeof(Int32));
// レコードの追加
// - DataRowを使った方法
DataRow row;
row = table.NewRow();
row["FirstName"] = "山田";
row["LastName"] = "太郎";
row["Age"] = 20;
// - DataTable.Rowsにobject[]型でレコードを追加
table.Rows.Add(new object[] {"佐藤","健",23});
// レコードの参照
DataRow frow = table[0];
string firstName = table[0]["FirstName"]; // 同じ
firstName = frow["FirstName"]; //
// レコードの走査
foreach(DataRow crow in table.Rows) {
// DataRowを使ってすべての行に対して処理
}
// フィルタリング(DataViewの使用)
DataView view = table.DefaultView;
view.RowFilter = "Age > 18"; // Ageフィールドの値が18以上のものだけを表示
for(int i=0;i<view.Count;i++){
DataRow row = view[i];
}
スキーマの作成
DataColumnを定義することで、DataTableにデータ構造のスキーマを定義します。
スキーマを与えると、正しくない値を含んだレコードをDataTableに追加した時にエラーの検出ができます。
DataTypeプロパティはカラムが格納することができる型を指定します。
DataColumn.DataTypeを指定することで強い型付けされたDataTableを作成することができます。
ただし、プログラマが作成した独自の型は指定することはできません。
- ReadOnlyプロパティ
- レコードの追加時のみ値を設定することができ、以降は読み取り専用となります。
- AutoIncrementプロパティ
- DataTypeがint型の場合に、レコードが追加されるたびに「0」から インクリメントされた値を自動的に格納していきます。 AutoIncrementSeed や AutoIncrementStep と併用することで インクリメントの開始値や加算値を調整できます。
- DefaultValueプロパティ
- カラムの初期値を設定します。
// カラムへスキームを設定するコード例
// - Decimal型
// - Nullを許容しない
// - コメントに「Price」
// - カラム名は「Price」
// - 値が未設定の場合は「25」とする
DataColumn column = new DataColumn();
column.DataType = System.Type.GetType("System.Decimal");
column.AllowDBNull = false;
column.Caption = "Price";
column.ColumnName = "Price";
column.DefaultValue = 25;
DataTableからのデータ取得方法
- DataTable.Rowsですべての行を走査する
- DataTable.Selectを使用して特定の行を取得する
- LINQを使用して特定の行を取得する(もっともオススメ)
データの抽出
DataTable table = new DataTable();
// ... tableの初期化
var rows = table.Select("Message = 'Test Message' OR FirstName like 'T%'","Message");
DataTable.Selectは遅いです。レコード数だけでなくフィールド数や各フィールドの型によって急激にパフォーマンスが悪くなることがあるので、使用する際はパフォーマンスを意識する必要があります。
データの抽出(LINQ)
C#3以降ではLINQを使用してDataTableやコレクションからデータを探すことができます。
パフォーマンスではLINQを使用する方法がもっとも早い(DataTable.Selectを使うよりも2倍以上早い)。
DataTable table = new DataTable();
// ... tableの初期化
var rows = (
from row in table.AsEnumerable()
let column0 = row.Field<string>("Message")
let column1 = row.Field<string>("FirstName")
where column0 == "Test Message" || column1.StartsWith("T")
orderby column0
select row
).ToArray();
データの集計
DataTableを駆使してデータの集計を行った方法を下記サイトで紹介しています。
ただ、DataViewを使ったり、重複を削除した後に集計を自分で計算するなど、スマートでないというイメージがあります。
C#3以降ならばLINQを使ってうまくできるハズですが。
DataTableの機能
ある状態からの変更点を取得する(トランザクション機能)
AcceptChanged()を呼び出してからの変更点をDataTableは記録しています。
GetChanged()を使って変更点を取得できます。引数に指定したものによって、削除されたレコード・追加されたレコード・変更されたレコードなどを取得できます。
DataTable table = new DataTable();
// ... テーブルの初期化
table.AcceptChanges(); // ここから
// ... テーブルの処理
DataTable deleted = table.GetChanged(DataRowState.Deleted); // 削除されたレコードが含まれたテーブル
if( daleted != null ) {
// 削除されたレコードに対する処理を行う
// deleted.RejectChanges()を呼び出すことで、この操作を無効にすることもできる。
}