在VB.net中检查值是否存在于DataTable中的最简/最快方法是什么?

13
我有一个 DataTable(目前有多列,但如果只取一列会更容易)。我想要检查一个 String 值是否存在于 DataTable 的某一列中。(由于我需要多次检查,因此希望速度相对较快)。
有没有好的方法来实现这个目标呢?每次迭代 DataTable 行似乎是一个不好的方法。我可以将该列转换为平面的 List/Array 格式,并使用内置函数吗?类似于 myStrList.Contains("value") 这样的东西?

2
你是否多次搜索相同且不变的DataTable?如果是这样,将数据放入某种索引数据结构(例如哈希表、树或排序数组(用于二进制搜索))中可能会更快。但是,如果表中的数据不断变化,则必须不断重建索引,这将使其变得毫无价值。 - Steven Doggart
@StevenDoggart 是的,一旦DT被获取,它就不会改变。你有使用哈希表/排序数组的例子吗?数据只是一个字符串列表。 - DisgruntledGoat
4个回答

24

您可以使用select来查找值是否存在。如果存在,则返回行,否则不会返回任何结果。以下是一些示例代码以帮助您。

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

12

如果您的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

您可能还希望考虑使用SortedListSortedDictionary类。这两种都是二叉树的实现。很难说在您特定的情况下哪一个选项会最快。这完全取决于数据类型、索引需要多频繁进行重建、搜索频率、DataTable中有多少行以及您需要对找到的项目做什么。最好的方法是在测试用例中尝试每个选项,看看哪个最适合您的需求。


1
谢谢,SortedList 很好用。不知道为什么标准的 List 不起作用 - 即使字符串在列表中,它仍然返回负索引。 - DisgruntledGoat
1
你会如何解决表格更新的问题(添加/删除行)? - Victor Zakharov
当你这样做时,你需要重新生成索引,或者你需要对索引进行相同的更改。例如,当添加新行时,您需要将其添加到DataTable和索引中,或者在添加行后重新生成整个索引。 - Steven Doggart
重新生成整个索引可能会耗费很多时间,特别是对于大型表格而言。如果您选择进行索引更新,那么您需要编写多少额外的代码呢? - Victor Zakharov
1
重新生成索引肯定会更慢。不知道您具体在做什么,很难回答这个问题。但是还有一件事需要考虑:您是否真的需要在断开的“DataTable”中完成所有这些操作?如果您连接到数据库,您可能会发现简单地重新查询数据库会更快。数据库已经拥有所有必要的索引,因此您不需要自己生成内存中的索引。 - Steven Doggart
请查看此帖子(https://stackoverflow.com/questions/68825034/search-and-replace-inside-string-column-in-datatable-is-slow)。我的DataTable中有大约90k行,对于给定的基于字符串的列(每行一个单词),通常整个列中有大约20-30个唯一的单词。需要用20-30个其他单词替换这些20-30个单词。在替换时还必须检查IsDBNull。这似乎表明,使用.Select和LINQ处理90k行的速度不太快。 - user16594857

10

为了提高性能,应该使用row filterDataTable.Rows.Find()而不是select(select不使用索引)。根据表格结构,特别是如果您查询的字段被索引(本地),这两种方法的性能都应比循环遍历所有行要快得多。在.NET中,一组字段需要成为PrimaryKey才能被索引。

如果您的字段没有被索引,我建议避免使用select和row filter,因为除了类复杂度的开销外,它们不能在编译时检查您的条件是否正确。如果它很长,您可能会花费大量时间调试它。

最好始终使用严格类型检查的方法。首先定义一个基础类型,然后可以定义此帮助程序方法,稍后可以将其转换为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")

如果您的行类有一个类型为StringMyColumn属性,则它变成:

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

以上方法的一个好处是,您可以将计算字段输入到检查条件中,因为这里的myColumn不需要匹配表/数据库中的物理myColumn


您还可以使用 AsEnumerable() (链接) 和 Any() 方法。 - Styxxy
有没有办法搜索行以查找一对值?也就是说,其中col2 =“somestring”且col3 =整数? - htm11h
如果我想使用它来查找类似于字符串的值,该怎么办?是 CheckValue(myTable, Function(x) x("myColumn") like "somevaluehere") 还是 CheckValue(myTable, Function(x) x("myColumn") = "*somevaluehere")? - user10349555
@Pr0x1mo:请使用页面顶部的“提问”按钮来提出新问题。 - Victor Zakharov
"For each" 是正确的循环语句,而不仅仅是 "For"。 - Abdul
显示剩余2条评论

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

1
这将检查数据表中的任何一行中,列“author”的值是否为“您要查找的值”。 - Divi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接