为什么在 VB.NET 中使用 OleDB 的文本框在 Datagridview 中进行筛选非常慢

问题描述 投票:0回答:1

我尝试在 VB.NET 中使用 OleDB 从文本框中筛选 Datagridview 非常慢。

打字也很慢,并且在事件文本框中删除或减少字符也变得很慢。请指导

我是否有任何解决方案或方法可以使其更快,或者我使用的代码是错误的。

数据库共有四千条记录

MS Access 数据库文件共享链接下方:

数据库

谢谢

Public Class Form1
    Private Function CreateConnection() As String
        Return ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\TRIAL.accdb;Persist Security Info=False;")
    End Function

 Private Purchase, Sales As New List(Of Invoice)
 Private PurchaseDetails, SaleDetails As New List(Of Detail)
 Private Card As List(Of StockCards)
 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        LoadData()
 End Sub
    Private Sub LoadData()
        Using Connection = New OleDbConnection(CreateConnection())
            Purchase = CType(Connection.Query(Of Invoice)("SELECT * FROM Purchase"), List(Of Invoice))
            PurchaseDetails = CType(Connection.Query(Of Detail)("SELECT * FROM PurchaseDetails"), List(Of Detail))
            Sales = CType(Connection.Query(Of Invoice)("SELECT * FROM Sales"), List(Of Invoice))
            SaleDetails = CType(Connection.Query(Of Detail)("SELECT * FROM SalesDetails"), List(Of Detail))
        End Using
        Dim ps = From p In Purchase
                 From pd In PurchaseDetails
                 Where pd.Invono = p.Invono
                 Select
        Invono = p.Invono,
         Invodate = p.InvoDate,
         p.Transaction,
         pd.CodeProduct,
         pd.Barcode,
         pd.Criteria1,
         pd.Criteria2,
         pd.Criteria3,
         pd.Criteria4,
         [IN] = pd.Qty,
         [OUT] = 0,
         BLC = pd.Qty
                 Order By Invodate, Invono
        Dim ss = From s In Sales
                 From sd In SaleDetails
                 Where sd.Invono = s.Invono
                 Select
             Invono = s.Invono,
             Invodate = s.InvoDate,
             s.Transaction,
             sd.CodeProduct,
             sd.Barcode,
             sd.Criteria1,
             sd.Criteria2,
             sd.Criteria3,
             sd.Criteria4,
             [IN] = 0,
             [OUT] =
             sd.Qty,
             BLC = -sd.Qty
                 Order By Invodate, Invono
        Dim Card_temp = ps.Union(ss).OrderBy(Function(w) w.Invodate).ThenBy(Function(w) w.Invono)

        'Dim Card As New List(Of StockCards)
        Card = New List(Of StockCards)
        Dim RunningBalance As Integer = 0
        For Each ct In Card_temp
            Dim sc As New StockCards
            With sc
                .Invono = ct.Invono
                .InvoDate = ct.Invodate
                .Transaction = ct.Transaction
                .CodeProduct = ct.CodeProduct
                .Barcode = ct.Barcode
                .Criteria1 = ct.Criteria1
                .Criteria2 = ct.Criteria2
                .Criteria3 = ct.Criteria3
                .Criteria4 = ct.Criteria4
                .IN = ct.IN
                .OUT = ct.OUT
                .BLC = RunningBalance + ct.BLC
                RunningBalance = .BLC
            End With
            Card.Add(sc)
        Next
        Datagridview1.DataSource = Card
    End Sub
  Private Sub DoFilter()
        If IsHandleCreated Then
            BeginInvoke(
            New Action(
            Sub()
                Dim dict As New Dictionary(Of String, String)

                If txtCriteria1.TextLength > 0 Then
                    dict.Add("Criteria1", txtCriteria1.Text)
                End If
                If txtCriteria2.TextLength > 0 Then
                    dict.Add("Criteria2", txtCriteria2.Text)
                End If
                If txtCriteria3.TextLength > 0 Then
                    dict.Add("Criteria3", txtCriteria3.Text)
                End If
                If txtCriteria4.TextLength > 0 Then
                    dict.Add("Criteria3", txtCriteria4.Text)
                End If
                ' Do the same for the other 3 boxes.
                Using Connection = New OleDbConnection(CreateConnection())
                    Purchase = CType(Connection.Query(Of Invoice)("SELECT * FROM Purchase"), List(Of Invoice))
                    PurchaseDetails = CType(Connection.Query(Of Detail)("SELECT * FROM PurchaseDetails"), List(Of Detail))
                    Sales = CType(Connection.Query(Of Invoice)("SELECT * FROM Sales"), List(Of Invoice))
                    SaleDetails = CType(Connection.Query(Of Detail)("SELECT * FROM SalesDetails"), List(Of Detail))
                End Using
                Dim ps = From p In Purchase
                         From pd In PurchaseDetails
                         Where pd.Invono = p.Invono
                         Select
        Invono = p.Invono,
         Invodate = p.InvoDate,
         p.Transaction,
         pd.CodeProduct,
         pd.Barcode,
         pd.Criteria1,
         pd.Criteria2,
         pd.Criteria3,
         pd.Criteria4,
         [IN] = pd.Qty,
         [OUT] = 0,
         BLC = pd.Qty
                         Order By Invodate, Invono
                Dim ss = From s In Sales
                         From sd In SaleDetails
                         Where sd.Invono = s.Invono
                         Select
             Invono = s.Invono,
             Invodate = s.InvoDate,
             s.Transaction,
             sd.CodeProduct,
             sd.Barcode,
             sd.Criteria1,
             sd.Criteria2,
             sd.Criteria3,
             sd.Criteria4,
             [IN] = 0,
             [OUT] =
             sd.Qty,
             BLC = -sd.Qty
                         Order By Invodate, Invono
                Dim Card_temp = ps.Union(ss).OrderBy(Function(w) w.Invodate).ThenBy(Function(w) w.Invono)
                'Dim Card As New List(Of StockCards)
                Card = New List(Of StockCards)
                Dim RunningBalance As Integer = 0
                For Each ct In Card_temp
                    Dim sc As New StockCards
                    With sc
                        .Invono = ct.Invono
                        .InvoDate = ct.Invodate
                        .Transaction = ct.Transaction
                        .CodeProduct = ct.CodeProduct
                        .Barcode = ct.Barcode
                        .Criteria1 = ct.Criteria1
                        .Criteria2 = ct.Criteria2
                        .Criteria3 = ct.Criteria3
                        .Criteria4 = ct.Criteria4
                        .IN = ct.IN
                        .OUT = ct.OUT
                        .BLC = RunningBalance + ct.BLC
                        RunningBalance = .BLC
                    End With
                    Card.Add(sc)
                Next
                Datagridview1.DataSource = If(dict.Any(),
                Card.Where(Function(CardCache) CardCache.IsFilterItem(dict)).ToList(),
                Card)
            End Sub))
            Return
     Else

            ' Handle the error case, or do nothing.
        End If
    End Sub
   Private Sub txtCriteria1_TextChanged(sender As Object, e As EventArgs) Handles txtCriteria1.TextChanged
        DoFilter()
    End Sub

    Private Sub txtCriteria2_TextChanged(sender As Object, e As EventArgs) Handles txtCriteria2.TextChanged
        DoFilter()
    End Sub

    Private Sub txtCriteria3_TextChanged(sender As Object, e As EventArgs) Handles txtCriteria3.TextChanged
        DoFilter()
    End Sub

    Private Sub txtCriteria4_TextChanged(sender As Object, e As EventArgs) Handles txtCriteria4.TextChanged
        DoFilter()
    End Sub
Public Class StockCards
    Public Property Invono() As String
    Public Property InvoDate() As Date
    Public Property Transaction() As String
    Public Property CodeProduct() As String
    Public Property Barcode() As String
    Public Property Criteria1() As String
    Public Property Criteria2() As String
    Public Property Criteria3() As String
    Public Property Criteria4() As String
    Public Property [IN] As Integer
    Public Property [OUT] As Integer
    Public Property BLC As Integer

    ' Replace `.Equals` with `.Contains` if needed...
    Public Function IsFilterItem(dict As IDictionary(Of String, String)) As Boolean
        Return dict.All(
            Function(kv) ContainsProperty(kv.Key) AndAlso
            GetPropertyValue(Of String)(kv.Key).
            Equals(kv.Value, StringComparison.CurrentCultureIgnoreCase))
    End Function
    Private Function GetPropertyValue(Of T)(propName As String) As T
        Return DirectCast(Me.GetType().GetProperty(propName)?.GetValue(Me), T)
    End Function

    Private Function ContainsProperty(propName As String) As Boolean
        Return Me.GetType().GetProperty(propName) IsNot Nothing
    End Function
End Class
Public Class Invoice
    Property Invono() As String
    Property InvoDate() As Date
    Property Transaction() As String
End Class

Public Class Detail
    Public Property Invono() As String
    Public Property CodeProduct() As String
    Public Property Barcode() As String
    Public Property Criteria1() As String
    Public Property Criteria2() As String
    Public Property Criteria3() As String
    Public Property Criteria4() As String
    Public Property Qty() As Integer
End Class

结果慢慢过滤datagridview

vb.net linq datagridview filtering
1个回答
0
投票

首先,我们需要发现并理解问题。您有一个数据库,并且有一个应用程序,每次文本框的值发生更改时都会在该数据库中进行搜索。所以,你的过程是:

每次[1]过滤器更改时,我们都会向数据库发送一个查询[2]

问题是这个过程很慢。所以,减少问题影响的方法就是

[1]减少查询频率 [2] 提高查询性能

假设您要搜索“B100”。在这种情况下,假设您没有输入错误并且是手写的,则可以在文本框中进行四个值更改。这意味着执行了 4 个查询,第一个搜索“B”,第二个搜索“B1”,第三个搜索“B10”,第四个搜索“B100”,而您只对最新的搜索,所有早期的搜索都与您无关。因此,设置一个计时器来在一段时间后执行搜索是有意义的。

例如,如果您有以下规则:

  • 更改过滤器时,您可以安排一个事件在 0.2 秒后发生
  • 如果在前 0.2 秒的等待结束之前发生过滤器更改,请重新安排等待时间
  • 等待时间过去后,向数据库发送单个查询

这样,您的搜索将在定义搜索词时执行,而不是在每次更改时重复执行,执行相关搜索并防止不相关的搜索。这只是降低频率的一种方法,但您也可以采取一些替代解决方案,只要明确搜索频率正在减慢系统速度即可。

现在,让我们看看查询速度可能的改进。正在看:

                Using Connection = New OleDbConnection(CreateConnection())
                    Purchase = CType(Connection.Query(Of Invoice)("SELECT * FROM Purchase"), List(Of Invoice))
                    PurchaseDetails = CType(Connection.Query(Of Detail)("SELECT * FROM PurchaseDetails"), List(Of Detail))
                    Sales = CType(Connection.Query(Of Invoice)("SELECT * FROM Sales"), List(Of Invoice))
                    SaleDetails = CType(Connection.Query(Of Detail)("SELECT * FROM SalesDetails"), List(Of Detail))
                End Using

您将这些查询转换为列表,因此您加载每个表的每条记录,并稍后应用排序和过滤器。将 1+ MB 加载到内存中是一个缓慢的过程,然后您还要对其进行过滤。相反,您需要创建

Query
并在实际运行之前应用
Where
OrderBy
以及其他内容。因此,您将仅加载和排序与此搜索相关的记录。

您可能还想对要过滤的字段建立索引,因此当您的数据库执行搜索时,它们将更加优化。

所以,总结一下:

  • 减少搜索频率
  • 确保过滤和排序是查询在数据库中执行之前的一部分
  • 在适当的地方创建索引

PurchaseDetails 和 SalesDetails 应借助其外键进行搜索,外键分别引用它们所属的购买和销售。另外,如果可能的话,最好只加载您需要的字段,因此您感兴趣的字段白名单通常会更高效,而不是

SELECT *
,因为有一些长字段,例如解释或类似内容并且您在网格中不需要该字段,那么您可以通过不将该字段加载到所有涉及的记录的内存中来赢得空间和时间。它也更安全,因为,如果您有在任何情况下都不想显示的秘密字段,那么最好根本不将它们发送到 UI。

© www.soinside.com 2019 - 2024. All rights reserved.