web-dev-qa-db-ja.com

VB.netのDataTableに値が存在するかどうかを確認する最も簡単な/最速の方法は?

私はDataTableを持っています(現在は複数の列を持っていますが、それが簡単になれば、1つの列を取得することもできます)。 Stringの値がDataTableの列に存在するかどうかを確認したいと思います。 (私は何度もやっているので、適度に速くしたいです。)

これを行う良い方法は何ですか?毎回DataTable行を反復処理することは、悪い方法のようです。列をフラット_List/Array_形式に変換し、組み込み関数を使用できますか? myStrList.Contains("value")のようなもの?

8
DisgruntledGoat

DataTableのデータがそれほど頻繁に変更されず、DataTableを複数回検索し、DataTableに多数の行が含まれている場合、それはおそらくデータ用に独自のインデックスを作成する方がはるかに高速です。

これを行う最も簡単な方法は、キー列でデータを並べ替え、並べ替えられたリストでバイナリ検索を実行できるようにすることです。たとえば、次のようなインデックスを作成できます。

Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As List(Of String)
    Dim index As New List(Of String)(table.Rows.Count)
    For Each row As DataRow in table.Rows
        index.Add(row(keyColumnIndex))
    Next
    index.Sort()
    Return index
End Function

次に、次のように、バイナリ検索で値がインデックスに存在するかどうかをすばやく確認できます。

Private Function ItemExists(index As List(Of String), key As String) As Boolean
    Dim index As Integer = index.BinarySearch(key)
    If index >= 0 Then
        Return True
    Else
        Return False
    End If
End Function

単純な文字列配列でも同じことができます。または、Dictionaryオブジェクト(ハッシュテーブルの実装)を使用して、DataTableのハッシュインデックスを作成することもできます。たとえば、次のようにします。

Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As Dictionary(Of String, DataRow)
    Dim index As New Dictionary(Of String, DataRow)(table.Rows.Count)
    For Each row As DataRow in table.Rows
        index(row(keyColumnIndex)) = row
    Next
    Return index
End Function

次に、次のように、特定のキーに一致するDataRowを取得できます。

Dim index As Dictionary(Of String, DataRow) = BuildIndex(myDataTable, myKeyColumnIndex)
Dim row As DataRow = Nothing
If index.TryGetValue(myKey, row) Then
   ' row was found, can now use row variable to access all the data in that row
Else
   ' row with that key does not exist
End If

SortedListまたはSortedDictionaryクラスの使用を検討することもできます。これらはどちらもバイナリツリーの実装です。これらのオプションのどれが特定のシナリオで最速になるかを言うのは難しいです。それはすべて、データのタイプ、インデックスを再構築する必要がある頻度、インデックスを検索する頻度、DataTableにある行の数、および見つかったアイテムをどのように処理する必要があるかによって異なります。最善の方法は、テストケースで1つずつ試して、必要なものに最適なものを確認することです。

10
Steven Doggart

selectを使用して、その値が存在するかどうかを確認できます。もしそうなら、それは行を返すか、そうしません。ここにあなたを助けるためのいくつかのサンプルコードがあります。

Dim foundRow() As DataRow
foundRow = dt.Select("SalesCategory='HP'")
15
Kiran1016

Select( selectはインデックスを使用しない )の代わりに row filter または DataTable.Rows.Find() を使用する必要があります。テーブル構造に応じて、特に問題のフィールドにインデックスが付けられている場合(ローカル)、どちらの方法のパフォーマンスも、すべての行をループするよりもはるかに高速です。 .NETでは、インデックスを作成するには、一連のフィールドを PrimaryKey にする必要があります。

フィールドにインデックスが付けられていない場合は、選択フィルターと行フィルターの両方を使用しません。これは、クラスの複雑さのオーバーヘッドを除いて、条件が正しいかどうかのコンパイル時間チェックを提供しないためです。長い場合、たまにデバッグに多くの時間を費やすことになります。

チェックは厳密に入力することをお勧めします。最初に基礎となる型を定義したら、このヘルパーメソッドを定義することもできます。これは、後でDataTableクラスの拡張メソッドに変換できます。

Shared Function CheckValue(myTable As DataTable, columnName As String, searchValue As String) As Boolean
  For row As DataRow In myTable.Rows
    If row(columnName) = searchValue Then Return True
  Next
  Return False
End Function

それのより一般的なバージョン:

Shared Function CheckValue(myTable As DataTable, checkFunc As Func(Of DataRow, Boolean)) As Boolean
  For Each row As DataRow In myTable.Rows
    If checkFunc(row) Then Return True
  Next
  Return False
End Function

とその使用法:

CheckValue(myTable, Function(x) x("myColumn") = "123")

行クラスにタイプMyColumnStringプロパティがある場合、それは次のようになります。

CheckValue(myTable, Function(x) x.myColumn = "123")

上記のアプローチの利点の1つは、ここでmyColumnがテーブル/データベース内の物理的なmyColumnと一致する必要がないため、計算されたフィールドをチェック条件にフィードできることです。

9
Neolisk
bool exists = dt.AsEnumerable().Where(c => c.Field<string>("Author").Equals("your lookup value")).Count() > 0;
1
Divi