加载2000+条数据库记录卡顿,求快速加载及进度显示方案
优化建议:提升加载速度并实现进度条显示
一、先把加载速度提上来
- 别用
select *,只查实际需要的字段
原SQL用select *会返回表中所有字段,但你代码里仅用到10个列,改成明确指定字段的SQL,能大幅减少数据库传输的数据量:Dim cmd As New OleDbCommand("select ID, storecode, check_date, check_num, check_name, check_memo, check_amount, date_release, date_deposit, status from tblstore ORDER by storecode, check_date, ID ASC", conn) - 放弃逐行添加,用DataAdapter批量加载
你现在循环dr.Read然后逐行调用DataGridView1.Rows.Add,每加一行都会触发UI刷新,效率极低。换成OleDbDataAdapter把数据一次性加载到DataTable,再绑定到DataGridView,速度会快很多。 - 暂停DataGridView的视觉刷新
加载数据前先暂停控件布局,加载完再恢复,避免重复刷新界面拖慢速度:DataGridView1.SuspendLayout() ' 这里做数据绑定操作 DataGridView1.ResumeLayout() - 用
Using管资源,别手动Dispose
数据库连接、命令、适配器这些资源,用Using语句自动释放,既安全又不用手动写Dispose,还能确保连接及时关闭,不会占着资源。
二、正确实现进度条,别搞不安全的跨线程操作
你之前设Control.CheckForIllegalCrossThreadCalls = False是偷懒的做法,很容易出问题,用BackgroundWorker自带的事件机制就能安全更新UI:
- 先开BackgroundWorker的进度报告开关
在设计器里把BackgroundWorker的WorkerReportsProgress和WorkerSupportsCancellation勾上,或者代码里设置:BackgroundWorker1.WorkerReportsProgress = True BackgroundWorker1.WorkerSupportsCancellation = True - 在DoWork里加载数据并报告进度
先查总记录数,然后在填充数据的时候实时计算进度,用ReportProgress发出去:Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork Dim dt As New DataTable() Try ' 用Using包裹连接,自动释放 Using conn As New OleDbConnection("你的数据库连接字符串") conn.Open() ' 先拿总记录数,用来算进度百分比 Dim countCmd As New OleDbCommand("select count(*) from tblstore", conn) Dim totalCount As Integer = CInt(countCmd.ExecuteScalar()) ' 查数据的SQL用刚才优化过的 Dim cmd As New OleDbCommand("select ID, storecode, check_date, check_num, check_name, check_memo, check_amount, date_release, date_deposit, status from tblstore ORDER by storecode, check_date, ID ASC", conn) Using adapter As New OleDbDataAdapter(cmd) ' 绑定填充进度事件,实时报告进度 AddHandler adapter.FillProgress, Sub(s, args) Dim progress = CInt((args.RecordsLoaded / totalCount) * 100) BackgroundWorker1.ReportProgress(progress) End Sub adapter.Fill(dt) End Using End Using e.Result = dt ' 把加载好的DataTable传给完成事件 Catch ex As Exception e.Result = ex ' 把异常信息传回去 End Try End Sub - ProgressChanged事件里更新进度条
这里是安全的UI线程,可以直接操作进度条:Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged ProgressBar1.Value = e.ProgressPercentage End Sub - RunWorkerCompleted事件里绑定数据
数据加载完后,在这个事件里把DataTable绑定到DataGridView,同时处理异常:Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted If e.Result IsNot Nothing Then If TypeOf e.Result Is Exception Then MsgBox(CType(e.Result, Exception).Message) Else DataGridView1.SuspendLayout() DataGridView1.DataSource = CType(e.Result, DataTable) DataGridView1.ResumeLayout() End If End If ProgressBar1.Value = 0 ' 加载完重置进度条 End Sub - 修正窗体加载事件
删掉那个不安全的Control.CheckForIllegalCrossThreadCalls = False,直接启动BackgroundWorker就行:Private Sub manageCheck_Load(sender As Object, e As EventArgs) Handles MyBase.Load BackgroundWorker1.RunWorkerAsync() End Sub
三、额外的小优化
- 关掉DataGridView的自动列生成
提前在设计器里把DataGridView的列配置好,设置每一列的DataPropertyName对应数据库字段,然后把AutoGenerateColumns设为False,避免自动生成列的开销:DataGridView1.AutoGenerateColumns = False ' 比如: IDColumn.DataPropertyName = "ID" StoreCodeColumn.DataPropertyName = "storecode" ' 其他列同理配置 - 数据量再大就开虚拟模式
如果以后数据超过几万条,可以把DataGridView的VirtualMode设为True,按需加载数据,能进一步降低内存占用和加载时间。
内容的提问来源于stack exchange,提问作者Nnek Lecxe
相关产品推荐
相关产品推荐

