DataTable自体にはソートをしてくれる機能がありませんが、DataTable.Select を応用することでソート処理ができます。
DataTable.Select() メソッドが DataTable の中でフィルタをかけるのに使えるということは(VB.Net)DataTableで特定の列の最大値を含む行を取得したい。(DataTable.Select)で紹介しました。
今回は純粋にソートだけを行う方法です。
問題は、DataTable.Select() メソッドは引数で指定された条件にあった DataRow 配列を返すという点です。DataTable 自体の内部のソートはしてくれません。
ということで、返り値の DataRow 配列を元に、新しい DataTable を作成するという方法をとりました。
コードはこんな感じです。
'ソート対象のテーブル作成Dim dtbl As New DataTable()
Dim col(1) As DataColumn
col(0) = New DataColumn("ID", Type.GetType("System.Int32"))
col(1) = New DataColumn("Age", Type.GetType("System.Int32"))
dtbl.Columns.AddRange(col)
'データ作成
For i As Integer = 0 To 5
Dim row As DataRow = dtbl.NewRow()
row("ID") = i
dtbl.Rows.Add(row)
Next
dtbl.Rows(0)("Age") = 30
dtbl.Rows(1)("Age") = 60
dtbl.Rows(2)("Age") = 40
dtbl.Rows(3)("Age") = 50
dtbl.Rows(4)("Age") = 20
dtbl.Rows(5)("Age") = 60
'ソート前確認
For Each row As DataRow In dtbl.Rows
Console.WriteLine("ID:" & row("ID") & " Age:" & row("Age"))
Next
Console.WriteLine("")
'DataTable.Select()を使いソート(第二引数にソート条件を書く)
Dim rows As DataRow() = dtbl.Select(Nothing, "Age DESC , ID ASC").Clone()
'ソート後の DataTable を用意
Dim dtblSrt As New DataTable()
'ソート前テーブルの情報をクローン
dtblSrt = dtbl.Clone()
'ソートされてる DataRow 配列をソート後の DataTable に追加
For Each row As DataRow In rows
dtblSrt.ImportRow(row)
Next
'ソート後確認
For Each row As DataRow In dtblSrt.Rows
Console.WriteLine("ID:" & row("ID") & " Age:" & row("Age"))
Next
上記例だとソート前にコンソール出力した結果はこうなります。
ID:0 Age:30
ID:1 Age:60
ID:2 Age:40
ID:3 Age:50
ID:4 Age:20
ID:5 Age:60
そして、ソート後にコンソール出力するとこうなります。
ID:1 Age:60
ID:5 Age:60
ID:3 Age:50
ID:2 Age:40
ID:0 Age:30
ID:4 Age:20
しゃんと指定したとおりにソートされてますね。
ただし、この方法だと新しい DataTable を作ったり、ループを使ってるため大量のデータのときに処理速度の問題や大量メモリ消費の問題がでてきます。
もっといい方法はないですかねぇ。。
ちなみに、@IT:[ADO.NET]データテーブル(DataTable)内のレコードをソートするには?では、DataRowView.Sort メソッドを使った方法が載せられてました。ソートの方法に DataRowView.Sort メソッドを使っているという点以外は、今回紹介した方法と似たようなことになってます。
また、DataTable.Select でのソートは値に NULL があった場合、Oracle での ORDER BY と結果が異なることがあるようなので、注意が必要です。詳しくは、DataTableおよびDataViewの注意点で取り上げられてました。