我尝试在 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
首先,我们需要发现并理解问题。您有一个数据库,并且有一个应用程序,每次文本框的值发生更改时都会在该数据库中进行搜索。所以,你的过程是:
每次[1]过滤器更改时,我们都会向数据库发送一个查询[2]
问题是这个过程很慢。所以,减少问题影响的方法就是
[1]减少查询频率 [2] 提高查询性能
假设您要搜索“B100”。在这种情况下,假设您没有输入错误并且是手写的,则可以在文本框中进行四个值更改。这意味着执行了 4 个查询,第一个搜索“B”,第二个搜索“B1”,第三个搜索“B10”,第四个搜索“B100”,而您只对最新的搜索,所有早期的搜索都与您无关。因此,设置一个计时器来在一段时间后执行搜索是有意义的。
例如,如果您有以下规则:
这样,您的搜索将在定义搜索词时执行,而不是在每次更改时重复执行,执行相关搜索并防止不相关的搜索。这只是降低频率的一种方法,但您也可以采取一些替代解决方案,只要明确搜索频率正在减慢系统速度即可。
现在,让我们看看查询速度可能的改进。正在看:
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。